diff options
author | Christian Hopps <chopps@labn.net> | 2023-03-08 23:22:09 +0100 |
---|---|---|
committer | Christian Hopps <chopps@labn.net> | 2023-03-22 03:08:32 +0100 |
commit | 1c84efe4fa8585df58a9b53459f94c47934f0786 (patch) | |
tree | 21609ad9048500a6b23c38ce373506d5d6aa6ac3 /mgmtd | |
parent | Merge pull request #13073 from donaldsharp/static_use_after_free (diff) | |
download | frr-1c84efe4fa8585df58a9b53459f94c47934f0786.tar.xz frr-1c84efe4fa8585df58a9b53459f94c47934f0786.zip |
mgmtd: Bringup MGMTD daemon and datastore module support
Features added in this commit:
1. Bringup/shutdown new management daemon 'mgmtd' along with FRR.
2. Support for Startup, Candidate and Running DBs.
3. Lock/Unlock DS feature using pthread lock.
4. Load config from a JSON file onto candidate DS.
5. Save config to a JSON file from running/candidate DS.
6. Dump candidate or running DS contents on the terminal or a file in
JSON/XML format.
7. Maintaining commit history (Full rollback support to be added in
future commits).
8. Addition of debug commands.
Co-authored-by: Yash Ranjan <ranjany@vmware.com>
Co-authored-by: Abhinay Ramesh <rabhinay@vmware.com>
Co-authored-by: Ujwal P <ujwalp@vmware.com>
Signed-off-by: Pushpasis Sarkar <pushpasis@gmail.com>
Diffstat (limited to 'mgmtd')
-rw-r--r-- | mgmtd/.gitignore | 1 | ||||
-rw-r--r-- | mgmtd/Makefile | 10 | ||||
-rw-r--r-- | mgmtd/mgmt.c | 49 | ||||
-rw-r--r-- | mgmtd/mgmt.h | 94 | ||||
-rw-r--r-- | mgmtd/mgmt_defines.h | 20 | ||||
-rw-r--r-- | mgmtd/mgmt_ds.c | 643 | ||||
-rw-r--r-- | mgmtd/mgmt_ds.h | 402 | ||||
-rw-r--r-- | mgmtd/mgmt_main.c | 271 | ||||
-rw-r--r-- | mgmtd/mgmt_memory.c | 21 | ||||
-rw-r--r-- | mgmtd/mgmt_memory.h | 16 | ||||
-rwxr-xr-x | mgmtd/mgmt_test_fe | 210 | ||||
-rw-r--r-- | mgmtd/mgmt_vty.c | 272 | ||||
-rw-r--r-- | mgmtd/mgmt_vty.c.safe | 506 | ||||
-rw-r--r-- | mgmtd/subdir.am | 42 |
14 files changed, 2557 insertions, 0 deletions
diff --git a/mgmtd/.gitignore b/mgmtd/.gitignore new file mode 100644 index 000000000..7ce107e93 --- /dev/null +++ b/mgmtd/.gitignore @@ -0,0 +1 @@ +mgmtd diff --git a/mgmtd/Makefile b/mgmtd/Makefile new file mode 100644 index 000000000..d69ec5f65 --- /dev/null +++ b/mgmtd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. mgmtd/mgmtd +%: ALWAYS + @$(MAKE) -s -C .. mgmtd/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/mgmtd/mgmt.c b/mgmtd/mgmt.c new file mode 100644 index 000000000..1901bde62 --- /dev/null +++ b/mgmtd/mgmt.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * FRR Management Daemon (MGMTD) program + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar + */ + +#include <zebra.h> +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_ds.h" +#include "mgmtd/mgmt_memory.h" + +bool mgmt_debug_be; +bool mgmt_debug_fe; +bool mgmt_debug_ds; +bool mgmt_debug_txn; + +/* MGMTD process wide configuration. */ +static struct mgmt_master mgmt_master; + +/* MGMTD process wide configuration pointer to export. */ +struct mgmt_master *mm; + +void mgmt_master_init(struct thread_master *master, const int buffer_size) +{ + memset(&mgmt_master, 0, sizeof(struct mgmt_master)); + + mm = &mgmt_master; + mm->master = master; + mm->terminating = false; + mm->socket_buffer = buffer_size; + mm->perf_stats_en = true; +} + +void mgmt_init(void) +{ + + /* Initialize datastores */ + mgmt_ds_init(mm); + + /* MGMTD VTY commands installation. */ + mgmt_vty_init(); +} + +void mgmt_terminate(void) +{ + mgmt_ds_destroy(); +} diff --git a/mgmtd/mgmt.h b/mgmtd/mgmt.h new file mode 100644 index 000000000..8d6425f84 --- /dev/null +++ b/mgmtd/mgmt.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD message definition header. + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_H +#define _FRR_MGMTD_H + +#include "vrf.h" + +#include "defaults.h" + +#include "mgmtd/mgmt_memory.h" +#include "mgmtd/mgmt_ds.h" + +#define MGMTD_VTY_PORT 2622 +#define MGMTD_SOCKET_BUF_SIZE 65535 + +extern bool mgmt_debug_be; +extern bool mgmt_debug_fe; +extern bool mgmt_debug_ds; +extern bool mgmt_debug_txn; + +/* + * MGMTD master for system wide configurations and variables. + */ +struct mgmt_master { + struct thread_master *master; + + /* How big should we set the socket buffer size */ + uint32_t socket_buffer; + + /* Datastores */ + struct mgmt_ds_ctx *running_ds; + struct mgmt_ds_ctx *candidate_ds; + struct mgmt_ds_ctx *oper_ds; + + bool terminating; /* global flag that sigint terminate seen */ + bool perf_stats_en; /* to enable performance stats measurement */ +}; + +extern struct mgmt_master *mm; + +/* + * Remove trailing separator from a string. + * + * str + * A null terminated string. + * + * sep + * Trailing character that needs to be removed. + */ +static inline void mgmt_remove_trailing_separator(char *str, char sep) +{ + size_t len; + + len = strlen(str); + if (len && str[len - 1] == sep) + str[len - 1] = '\0'; +} + +/* Prototypes. */ +extern void mgmt_terminate(void); +extern void mgmt_reset(void); +extern time_t mgmt_clock(void); + +extern int mgmt_config_write(struct vty *vty); + +extern void mgmt_master_init(struct thread_master *master, + const int buffer_size); + +extern void mgmt_init(void); +extern void mgmt_vty_init(void); + +static inline char *mgmt_realtime_to_string(struct timeval *tv, char *buf, + size_t sz) +{ + char tmp[50]; + struct tm *lm; + + lm = localtime((const time_t *)&tv->tv_sec); + if (lm) { + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", lm); + snprintf(buf, sz, "%s.%06lu", tmp, + (unsigned long int)tv->tv_usec); + } + + return buf; +} + +#endif /* _FRR_MGMTD_H */ diff --git a/mgmtd/mgmt_defines.h b/mgmtd/mgmt_defines.h new file mode 100644 index 000000000..7ff803bf1 --- /dev/null +++ b/mgmtd/mgmt_defines.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD public defines. + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_DEFINES_H +#define _FRR_MGMTD_DEFINES_H + +#include "yang.h" + +#define MGMTD_CLIENT_NAME_MAX_LEN 32 + +#define MGMTD_MAX_XPATH_LEN XPATH_MAXLEN + +#define MGMTD_MAX_YANG_VALUE_LEN YANG_VALUE_MAXLEN + +#endif /* _FRR_MGMTD_DEFINES_H */ diff --git a/mgmtd/mgmt_ds.c b/mgmtd/mgmt_ds.c new file mode 100644 index 000000000..85ff1da7d --- /dev/null +++ b/mgmtd/mgmt_ds.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Datastores + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> +#include "md5.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_memory.h" +#include "mgmtd/mgmt_ds.h" +#include "libyang/libyang.h" + +#ifdef REDIRECT_DEBUG_TO_STDERR +#define MGMTD_DS_DBG(fmt, ...) \ + fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#define MGMTD_DS_ERR(fmt, ...) \ + fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__) +#else /* REDIRECT_DEBUG_TO_STDERR */ +#define MGMTD_DS_DBG(fmt, ...) \ + do { \ + if (mgmt_debug_ds) \ + zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) +#define MGMTD_DS_ERR(fmt, ...) \ + zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) +#endif /* REDIRECT_DEBUG_TO_STDERR */ + +struct mgmt_ds_ctx { + enum mgmt_datastore_id ds_id; + int lock; /* 0 unlocked, >0 read locked < write locked */ + + bool config_ds; + + union { + struct nb_config *cfg_root; + struct lyd_node *dnode_root; + } root; +}; + +const char *mgmt_ds_names[MGMTD_DS_MAX_ID + 1] = { + MGMTD_DS_NAME_NONE, /* MGMTD_DS_NONE */ + MGMTD_DS_NAME_RUNNING, /* MGMTD_DS_RUNNING */ + MGMTD_DS_NAME_CANDIDATE, /* MGMTD_DS_CANDIDATE */ + MGMTD_DS_NAME_OPERATIONAL, /* MGMTD_DS_OPERATIONAL */ + "Unknown/Invalid", /* MGMTD_DS_ID_MAX */ +}; + +static struct mgmt_master *mgmt_ds_mm; +static struct mgmt_ds_ctx running, candidate, oper; + +/* Dump the data tree of the specified format in the file pointed by the path */ +static int mgmt_ds_dump_in_memory(struct mgmt_ds_ctx *ds_ctx, + const char *base_xpath, LYD_FORMAT format, + struct ly_out *out) +{ + struct lyd_node *root; + uint32_t options = 0; + + if (base_xpath[0] == '\0') + root = ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root; + else + root = yang_dnode_get(ds_ctx->config_ds + ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + base_xpath); + if (!root) + return -1; + + options = ds_ctx->config_ds ? LYD_PRINT_WD_TRIM : + LYD_PRINT_WD_EXPLICIT; + + if (base_xpath[0] == '\0') + lyd_print_all(out, root, format, options); + else + lyd_print_tree(out, root, format, options); + + return 0; +} + +static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, + struct mgmt_ds_ctx *dst) +{ + struct lyd_node *dst_dnode, *src_dnode; + struct ly_out *out; + + if (!src || !dst) + return -1; + MGMTD_DS_DBG("Replacing %d with %d", dst->ds_id, src->ds_id); + + src_dnode = src->config_ds ? src->root.cfg_root->dnode + : dst->root.dnode_root; + dst_dnode = dst->config_ds ? dst->root.cfg_root->dnode + : dst->root.dnode_root; + + if (dst_dnode) + yang_dnode_free(dst_dnode); + + /* Not using nb_config_replace as the oper ds does not contain nb_config + */ + dst_dnode = yang_dnode_dup(src_dnode); + if (dst->config_ds) + dst->root.cfg_root->dnode = dst_dnode; + else + dst->root.dnode_root = dst_dnode; + + if (dst->ds_id == MGMTD_DS_RUNNING) { + if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out) + == LY_SUCCESS) + mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out); + ly_out_free(out, NULL, 0); + } + + /* TODO: Update the versions if nb_config present */ + + return 0; +} + +static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, + struct mgmt_ds_ctx *dst) +{ + int ret; + struct lyd_node **dst_dnode, *src_dnode; + struct ly_out *out; + + if (!src || !dst) + return -1; + + MGMTD_DS_DBG("Merging DS %d with %d", dst->ds_id, src->ds_id); + + src_dnode = src->config_ds ? src->root.cfg_root->dnode + : dst->root.dnode_root; + dst_dnode = dst->config_ds ? &dst->root.cfg_root->dnode + : &dst->root.dnode_root; + ret = lyd_merge_siblings(dst_dnode, src_dnode, 0); + if (ret != 0) { + MGMTD_DS_ERR("lyd_merge() failed with err %d", ret); + return ret; + } + + if (dst->ds_id == MGMTD_DS_RUNNING) { + if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out) + == LY_SUCCESS) + mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out); + ly_out_free(out, NULL, 0); + } + + return 0; +} + +static int mgmt_ds_load_cfg_from_file(const char *filepath, + struct lyd_node **dnode) +{ + LY_ERR ret; + + *dnode = NULL; + ret = lyd_parse_data_path(ly_native_ctx, filepath, LYD_JSON, + LYD_PARSE_STRICT, 0, dnode); + + if (ret != LY_SUCCESS) { + if (*dnode) + yang_dnode_free(*dnode); + return -1; + } + + return 0; +} + +int mgmt_ds_init(struct mgmt_master *mm) +{ + struct lyd_node *root; + + if (mgmt_ds_mm || mm->running_ds || mm->candidate_ds || mm->oper_ds) + assert(!"MGMTD: Call ds_init only once!"); + + /* Use Running DS from NB module??? */ + if (!running_config) + assert(!"MGMTD: Call ds_init after frr_init only!"); + + if (mgmt_ds_load_cfg_from_file(MGMTD_STARTUP_DS_FILE_PATH, &root) + == 0) { + nb_config_free(running_config); + running_config = nb_config_new(root); + } + + running.root.cfg_root = running_config; + running.config_ds = true; + running.ds_id = MGMTD_DS_RUNNING; + + candidate.root.cfg_root = nb_config_dup(running.root.cfg_root); + candidate.config_ds = true; + candidate.ds_id = MGMTD_DS_CANDIDATE; + + oper.root.dnode_root = yang_dnode_new(ly_native_ctx, true); + oper.config_ds = false; + oper.ds_id = MGMTD_DS_OPERATIONAL; + + mm->running_ds = &running; + mm->candidate_ds = &candidate; + mm->oper_ds = &oper; + mgmt_ds_mm = mm; + + return 0; +} + +void mgmt_ds_destroy(void) +{ + + /* + * TODO: Free the datastores. + */ + +} + +struct mgmt_ds_ctx *mgmt_ds_get_ctx_by_id(struct mgmt_master *mm, + enum mgmt_datastore_id ds_id) +{ + switch (ds_id) { + case MGMTD_DS_CANDIDATE: + return (mm->candidate_ds); + case MGMTD_DS_RUNNING: + return (mm->running_ds); + case MGMTD_DS_OPERATIONAL: + return (mm->oper_ds); + case MGMTD_DS_NONE: + case MGMTD_DS_MAX_ID: + default: + return 0; + } + + return 0; +} + +bool mgmt_ds_is_config(struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) + return false; + + return ds_ctx->config_ds; +} + +int mgmt_ds_read_lock(struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) + return EINVAL; + if (ds_ctx->lock < 0) + return EBUSY; + ++ds_ctx->lock; + return 0; +} + +int mgmt_ds_write_lock(struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) + return EINVAL; + if (ds_ctx->lock != 0) + return EBUSY; + ds_ctx->lock = -1; + return 0; +} + +int mgmt_ds_unlock(struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) + return EINVAL; + if (ds_ctx->lock > 0) + --ds_ctx->lock; + else if (ds_ctx->lock < 0) { + assert(ds_ctx->lock == -1); + ds_ctx->lock = 0; + } else { + assert(ds_ctx->lock != 0); + return EINVAL; + } + return 0; +} + +int mgmt_ds_merge_dss(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx, bool updt_cmt_rec) +{ + if (mgmt_ds_merge_src_with_dst_ds(src_ds_ctx, dst_ds_ctx) != 0) + return -1; + + return 0; +} + +int mgmt_ds_copy_dss(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx, bool updt_cmt_rec) +{ + if (mgmt_ds_replace_dst_with_src_ds(src_ds_ctx, dst_ds_ctx) != 0) + return -1; + + return 0; +} + +int mgmt_ds_dump_ds_to_file(char *file_name, struct mgmt_ds_ctx *ds_ctx) +{ + struct ly_out *out; + int ret = 0; + + if (ly_out_new_filepath(file_name, &out) == LY_SUCCESS) { + ret = mgmt_ds_dump_in_memory(ds_ctx, "", LYD_JSON, out); + ly_out_free(out, NULL, 0); + } + + return ret; +} + +struct nb_config *mgmt_ds_get_nb_config(struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) + return NULL; + + return ds_ctx->config_ds ? ds_ctx->root.cfg_root : NULL; +} + +static int mgmt_walk_ds_nodes( + struct mgmt_ds_ctx *ds_ctx, char *base_xpath, + struct lyd_node *base_dnode, + void (*mgmt_ds_node_iter_fn)(struct mgmt_ds_ctx *ds_ctx, char *xpath, + struct lyd_node *node, + struct nb_node *nb_node, void *ctx), + void *ctx, char *xpaths[], int *num_nodes, bool childs_as_well, + bool alloc_xp_copy) +{ + uint32_t indx; + char *xpath, *xpath_buf, *iter_xp; + int ret, num_left = 0, num_found = 0; + struct lyd_node *dnode; + struct nb_node *nbnode; + bool alloc_xp = false; + + if (xpaths) + assert(num_nodes); + + if (num_nodes && !*num_nodes) + return 0; + + if (num_nodes) { + num_left = *num_nodes; + MGMTD_DS_DBG(" -- START: num_left:%d", num_left); + *num_nodes = 0; + } + + MGMTD_DS_DBG(" -- START: Base: %s", base_xpath); + + if (!base_dnode) + base_dnode = yang_dnode_get( + ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + base_xpath); + if (!base_dnode) + return -1; + + if (mgmt_ds_node_iter_fn) { + /* + * In case the caller is interested in getting a copy + * of the xpath for themselves (by setting + * 'alloc_xp_copy' to 'true') we make a copy for the + * caller and pass it. Else we pass the original xpath + * buffer. + * + * NOTE: In such case caller will have to take care of + * the copy later. + */ + iter_xp = alloc_xp_copy ? strdup(base_xpath) : base_xpath; + + nbnode = (struct nb_node *)base_dnode->schema->priv; + (*mgmt_ds_node_iter_fn)(ds_ctx, iter_xp, base_dnode, nbnode, + ctx); + } + + if (num_nodes) { + (*num_nodes)++; + num_left--; + } + + /* If the base_xpath points to leaf node, we can skip the tree walk */ + if (base_dnode->schema->nodetype & LYD_NODE_TERM) + return 0; + + indx = 0; + LY_LIST_FOR (lyd_child(base_dnode), dnode) { + assert(dnode->schema && dnode->schema->priv); + nbnode = (struct nb_node *)dnode->schema->priv; + + xpath = NULL; + if (xpaths) { + if (!xpaths[*num_nodes]) { + alloc_xp = true; + xpaths[*num_nodes] = + (char *)calloc(1, MGMTD_MAX_XPATH_LEN); + } + xpath = lyd_path(dnode, LYD_PATH_STD, + xpaths[*num_nodes], + MGMTD_MAX_XPATH_LEN); + } else { + alloc_xp = true; + xpath_buf = (char *)calloc(1, MGMTD_MAX_XPATH_LEN); + (void) lyd_path(dnode, LYD_PATH_STD, xpath_buf, + MGMTD_MAX_XPATH_LEN); + xpath = xpath_buf; + } + + assert(xpath); + MGMTD_DS_DBG(" -- XPATH: %s", xpath); + + if (!childs_as_well) + continue; + + if (num_nodes) + num_found = num_left; + + ret = mgmt_walk_ds_nodes(ds_ctx, xpath, dnode, + mgmt_ds_node_iter_fn, ctx, + xpaths ? &xpaths[*num_nodes] : NULL, + num_nodes ? &num_found : NULL, + childs_as_well, alloc_xp_copy); + + if (num_nodes) { + num_left -= num_found; + (*num_nodes) += num_found; + } + + if (alloc_xp) + free(xpath); + + if (ret != 0) + break; + + indx++; + } + + + if (num_nodes) { + MGMTD_DS_DBG(" -- END: *num_nodes:%d, num_left:%d", *num_nodes, + num_left); + } + + return 0; +} + +int mgmt_ds_lookup_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath, + char *dxpaths[], int *num_nodes, + bool get_childs_as_well, bool alloc_xp_copy) +{ + char base_xpath[MGMTD_MAX_XPATH_LEN]; + + if (!ds_ctx || !num_nodes) + return -1; + + if (xpath[0] == '.' && xpath[1] == '/') + xpath += 2; + + strlcpy(base_xpath, xpath, sizeof(base_xpath)); + mgmt_remove_trailing_separator(base_xpath, '/'); + + return (mgmt_walk_ds_nodes(ds_ctx, base_xpath, NULL, NULL, NULL, + dxpaths, num_nodes, get_childs_as_well, + alloc_xp_copy)); +} + +struct lyd_node *mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx *ds_ctx, + const char *xpath) +{ + if (!ds_ctx) + return NULL; + + return yang_dnode_get(ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + xpath); +} + +int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath) +{ + struct nb_node *nb_node; + struct lyd_node *dnode, *dep_dnode; + char dep_xpath[XPATH_MAXLEN]; + + if (!ds_ctx) + return -1; + + nb_node = nb_node_find(xpath); + + dnode = yang_dnode_get(ds_ctx->config_ds + ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + xpath); + + if (!dnode) + /* + * Return a special error code so the caller can choose + * whether to ignore it or not. + */ + return NB_ERR_NOT_FOUND; + /* destroy dependant */ + if (nb_node->dep_cbs.get_dependant_xpath) { + nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath); + + dep_dnode = yang_dnode_get( + ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + dep_xpath); + if (dep_dnode) + lyd_free_tree(dep_dnode); + } + lyd_free_tree(dnode); + + return 0; +} + +int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *dst, + const char *file_path, bool merge) +{ + struct lyd_node *iter; + struct mgmt_ds_ctx parsed; + + if (!dst) + return -1; + + if (mgmt_ds_load_cfg_from_file(file_path, &iter) != 0) { + MGMTD_DS_ERR("Failed to load config from the file %s", + file_path); + return -1; + } + + parsed.root.cfg_root = nb_config_new(iter); + parsed.config_ds = true; + parsed.ds_id = dst->ds_id; + + if (merge) + mgmt_ds_merge_src_with_dst_ds(&parsed, dst); + else + mgmt_ds_replace_dst_with_src_ds(&parsed, dst); + + nb_config_free(parsed.root.cfg_root); + + return 0; +} + +int mgmt_ds_iter_data(struct mgmt_ds_ctx *ds_ctx, char *base_xpath, + void (*mgmt_ds_node_iter_fn)(struct mgmt_ds_ctx *ds_ctx, + char *xpath, + struct lyd_node *node, + struct nb_node *nb_node, + void *ctx), + void *ctx, bool alloc_xp_copy) +{ + int ret; + char xpath[MGMTD_MAX_XPATH_LEN]; + struct lyd_node *base_dnode = NULL; + struct lyd_node *node; + + if (!ds_ctx) + return -1; + + mgmt_remove_trailing_separator(base_xpath, '/'); + + strlcpy(xpath, base_xpath, sizeof(xpath)); + + MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_ctx->ds_id); + + /* If the base_xpath is empty then crawl the sibblings */ + if (xpath[0] == '\0') { + base_dnode = ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root; + + /* get first top-level sibling */ + while (base_dnode->parent) + base_dnode = lyd_parent(base_dnode); + + while (base_dnode->prev->next) + base_dnode = base_dnode->prev; + + LY_LIST_FOR (base_dnode, node) { + ret = mgmt_walk_ds_nodes( + ds_ctx, xpath, node, mgmt_ds_node_iter_fn, + ctx, NULL, NULL, true, alloc_xp_copy); + } + } else + ret = mgmt_walk_ds_nodes(ds_ctx, xpath, base_dnode, + mgmt_ds_node_iter_fn, ctx, NULL, NULL, + true, alloc_xp_copy); + + return ret; +} + +void mgmt_ds_dump_tree(struct vty *vty, struct mgmt_ds_ctx *ds_ctx, + const char *xpath, FILE *f, LYD_FORMAT format) +{ + struct ly_out *out; + char *str; + char base_xpath[MGMTD_MAX_XPATH_LEN] = {0}; + + if (!ds_ctx) { + vty_out(vty, " >>>>> Datastore Not Initialized!\n"); + return; + } + + if (xpath) { + strlcpy(base_xpath, xpath, MGMTD_MAX_XPATH_LEN); + mgmt_remove_trailing_separator(base_xpath, '/'); + } + + if (f) + ly_out_new_file(f, &out); + else + ly_out_new_memory(&str, 0, &out); + + mgmt_ds_dump_in_memory(ds_ctx, base_xpath, format, out); + + if (!f) + vty_out(vty, "%s\n", str); + + ly_out_free(out, NULL, 0); +} + +void mgmt_ds_status_write_one(struct vty *vty, struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) { + vty_out(vty, " >>>>> Datastore Not Initialized!\n"); + return; + } + + vty_out(vty, " DS: %s\n", mgmt_ds_id2name(ds_ctx->ds_id)); + vty_out(vty, " DS-Hndl: \t\t\t%p\n", ds_ctx); + vty_out(vty, " Config: \t\t\t%s\n", + ds_ctx->config_ds ? "True" : "False"); +} + +void mgmt_ds_status_write(struct vty *vty) +{ + vty_out(vty, "MGMTD Datastores\n"); + + mgmt_ds_status_write_one(vty, mgmt_ds_mm->running_ds); + + mgmt_ds_status_write_one(vty, mgmt_ds_mm->candidate_ds); + + mgmt_ds_status_write_one(vty, mgmt_ds_mm->oper_ds); +} diff --git a/mgmtd/mgmt_ds.h b/mgmtd/mgmt_ds.h new file mode 100644 index 000000000..61fb7dd4f --- /dev/null +++ b/mgmtd/mgmt_ds.h @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Datastores + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_DS_H_ +#define _FRR_MGMTD_DS_H_ + +#include "northbound.h" + +#include "mgmtd/mgmt_defines.h" + +#define MGMTD_MAX_NUM_DSNODES_PER_BATCH 128 + +#define MGMTD_DS_NAME_MAX_LEN 32 +#define MGMTD_DS_NAME_NONE "none" +#define MGMTD_DS_NAME_RUNNING "running" +#define MGMTD_DS_NAME_CANDIDATE "candidate" +#define MGMTD_DS_NAME_OPERATIONAL "operational" + +#define MGMTD_STARTUP_DS_FILE_PATH DAEMON_DB_DIR "/frr_startup.json" + +#define FOREACH_MGMTD_DS_ID(id) \ + for ((id) = MGMTD_DS_NONE; (id) < MGMTD_DS_MAX_ID; (id)++) + +#define MGMTD_MAX_COMMIT_LIST 10 +#define MGMTD_MD5_HASH_LEN 16 +#define MGMTD_MD5_HASH_STR_HEX_LEN 33 + +#define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json" +#define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat" +#define MGMTD_COMMIT_TIME_STR_LEN 100 + +struct mgmt_master; + +extern struct nb_config *running_config; + +struct mgmt_ds_ctx; + +/* + * Datastore-Id: For now defined here. Eventually will be + * defined as part of MGMTD Front-End interface. + */ +enum mgmt_datastore_id { + MGMTD_DS_NONE = 0, + MGMTD_DS_RUNNING, + MGMTD_DS_CANDIDATE, + MGMTD_DS_OPERATIONAL, + MGMTD_DS_MAX_ID +}; + +typedef void (*mgmt_ds_node_iter_fn)(uint64_t ds_hndl, char *xpath, + struct lyd_node *node, + struct nb_node *nb_node, void *ctx); + +/*************************************************************** + * Global data exported + ***************************************************************/ + +extern const char *mgmt_ds_names[MGMTD_DS_MAX_ID + 1]; + +/* + * Convert datastore ID to datastore name. + * + * id + * Datastore ID. + * + * Returns: + * Datastore name. + */ +static inline const char *mgmt_ds_id2name(enum mgmt_datastore_id id) +{ + if (id > MGMTD_DS_MAX_ID) + id = MGMTD_DS_MAX_ID; + return mgmt_ds_names[id]; +} + +/* + * Convert datastore name to datastore ID. + * + * id + * Datastore name. + * + * Returns: + * Datastore ID. + */ +static inline enum mgmt_datastore_id mgmt_ds_name2id(const char *name) +{ + enum mgmt_datastore_id id; + + FOREACH_MGMTD_DS_ID (id) { + if (!strncmp(mgmt_ds_names[id], name, MGMTD_DS_NAME_MAX_LEN)) + return id; + } + + return MGMTD_DS_NONE; +} + +/* + * Convert datastore ID to datastore name. + * + * similar to above funtion. + */ +static inline enum mgmt_datastore_id mgmt_get_ds_id_by_name(const char *ds_name) +{ + if (!strncmp(ds_name, "candidate", sizeof("candidate"))) + return MGMTD_DS_CANDIDATE; + else if (!strncmp(ds_name, "running", sizeof("running"))) + return MGMTD_DS_RUNNING; + else if (!strncmp(ds_name, "operational", sizeof("operational"))) + return MGMTD_DS_OPERATIONAL; + return MGMTD_DS_NONE; +} + +/* + * Appends trail wildcard '/' '*' to a given xpath. + * + * xpath + * YANG xpath. + * + * path_len + * xpath length. + */ +static inline void mgmt_xpath_append_trail_wildcard(char *xpath, + size_t *xpath_len) +{ + if (!xpath || !xpath_len) + return; + + if (!*xpath_len) + *xpath_len = strlen(xpath); + + if (*xpath_len > 2 && *xpath_len < MGMTD_MAX_XPATH_LEN - 2) { + if (xpath[*xpath_len - 1] == '/') { + xpath[*xpath_len] = '*'; + xpath[*xpath_len + 1] = 0; + (*xpath_len)++; + } else if (xpath[*xpath_len - 1] != '*') { + xpath[*xpath_len] = '/'; + xpath[*xpath_len + 1] = '*'; + xpath[*xpath_len + 2] = 0; + (*xpath_len) += 2; + } + } +} + +/* + * Removes trail wildcard '/' '*' from a given xpath. + * + * xpath + * YANG xpath. + * + * path_len + * xpath length. + */ +static inline void mgmt_xpath_remove_trail_wildcard(char *xpath, + size_t *xpath_len) +{ + if (!xpath || !xpath_len) + return; + + if (!*xpath_len) + *xpath_len = strlen(xpath); + + if (*xpath_len > 2 && xpath[*xpath_len - 2] == '/' + && xpath[*xpath_len - 1] == '*') { + xpath[*xpath_len - 2] = 0; + (*xpath_len) -= 2; + } +} + +/* Initialise datastore */ +extern int mgmt_ds_init(struct mgmt_master *cm); + +/* Destroy datastore */ +extern void mgmt_ds_destroy(void); + +/* + * Get datastore handler by ID + * + * mm + * Management master structure. + * + * ds_id + * Datastore ID. + * + * Returns: + * Datastore context (Holds info about ID, lock, root node etc). + */ +extern struct mgmt_ds_ctx *mgmt_ds_get_ctx_by_id(struct mgmt_master *mm, + enum mgmt_datastore_id ds_id); + +/* + * Check if a given datastore is config ds + */ +extern bool mgmt_ds_is_config(struct mgmt_ds_ctx *ds_ctx); + +/* + * Acquire read lock to a ds given a ds_handle + */ +extern int mgmt_ds_read_lock(struct mgmt_ds_ctx *ds_ctx); + +/* + * Acquire write lock to a ds given a ds_handle + */ +extern int mgmt_ds_write_lock(struct mgmt_ds_ctx *ds_ctx); + +/* + * Remove a lock from ds given a ds_handle + */ +extern int mgmt_ds_unlock(struct mgmt_ds_ctx *ds_ctx); + +/* + * Merge two datastores. + * + * src_ds + * Source datastore handle. + * + * dst_ds + * Destination datastore handle. + * + * update_cmd_rec + * TRUE if need to update commit record, FALSE otherwise. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_merge_dss(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx, + bool update_cmt_rec); + +/* + * Copy from source to destination datastore. + * + * src_ds + * Source datastore handle (ds to be copied from). + * + * dst_ds + * Destination datastore handle (ds to be copied to). + * + * update_cmd_rec + * TRUE if need to update commit record, FALSE otherwise. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_copy_dss(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx, + bool update_cmt_rec); + +/* + * Fetch northbound configuration for a given datastore context. + */ +extern struct nb_config *mgmt_ds_get_nb_config(struct mgmt_ds_ctx *ds_ctx); + +/* + * Lookup YANG data nodes. + * + * ds_ctx + * Datastore context. + * + * xpath + * YANG base xpath. + * + * dxpaths + * Out param - array of YANG data xpaths. + * + * num_nodes + * In-out param - number of YANG data xpaths. + * Note - Caller should init this to the size of the array + * provided in dxpaths. + * On return this will have the actual number of xpaths + * being returned. + * + * get_childs_as_well + * TRUE if child nodes needs to be fetched as well, FALSE otherwise. + * + * alloc_xp_copy + * TRUE if the caller is interested in getting a copy of the xpath. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_lookup_data_nodes(struct mgmt_ds_ctx *ds_ctx, + const char *xpath, char *dxpaths[], + int *num_nodes, bool get_childs_as_well, + bool alloc_xp_copy); + +/* + * Find YANG data node given a datastore handle YANG xpath. + */ +extern struct lyd_node * +mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx *ds_ctx, + const char *xpath); + +/* + * Delete YANG data node given a datastore handle and YANG xpath. + */ +extern int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, + const char *xpath); + +/* + * Iterate over datastore data. + * + * ds_ctx + * Datastore context. + * + * base_xpath + * Base YANG xpath from where needs to be iterated. + * + * iter_fn + * function that will be called during each iteration. + * + * ctx + * User defined opaque value normally used to pass + * reference to some user private context that will + * be passed to the iterator function provided in + * 'iter_fn'. + * + * alloc_xp_copy + * TRUE if the caller is interested in getting a copy of the xpath. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_iter_data( + struct mgmt_ds_ctx *ds_ctx, char *base_xpath, + void (*mgmt_ds_node_iter_fn)(struct mgmt_ds_ctx *ds_ctx, char *xpath, + struct lyd_node *node, + struct nb_node *nb_node, void *ctx), + void *ctx, bool alloc_xp_copy); + +/* + * Load config to datastore from a file. + * + * ds_ctx + * Datastore context. + * + * file_path + * File path of the configuration file. + * + * merge + * TRUE if you want to merge with existing config, + * FALSE if you want to replace with existing config + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *ds_ctx, + const char *file_path, bool merge); + +/* + * Dump the data tree to a file with JSON/XML format. + * + * vty + * VTY context. + * + * ds_ctx + * Datastore context. + * + * xpath + * Base YANG xpath from where data needs to be dumped. + * + * f + * File pointer to where data to be dumped. + * + * format + * JSON/XML + */ +extern void mgmt_ds_dump_tree(struct vty *vty, struct mgmt_ds_ctx *ds_ctx, + const char *xpath, FILE *f, LYD_FORMAT format); + +/* + * Dump the complete data tree to a file with JSON format. + * + * file_name + * File path to where data to be dumped. + * + * ds + * Datastore context. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_dump_ds_to_file(char *file_name, + struct mgmt_ds_ctx *ds_ctx); + +/* + * Dump information about specific datastore. + */ +extern void mgmt_ds_status_write_one(struct vty *vty, + struct mgmt_ds_ctx *ds_ctx); + +/* + * Dump information about all the datastores. + */ +extern void mgmt_ds_status_write(struct vty *vty); + +#endif /* _FRR_MGMTD_DS_H_ */ diff --git a/mgmtd/mgmt_main.c b/mgmtd/mgmt_main.c new file mode 100644 index 000000000..050367e33 --- /dev/null +++ b/mgmtd/mgmt_main.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Main routine of mgmt. + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar + */ + +#include <zebra.h> +#include "lib/version.h" +#include "routemap.h" +#include "filter.h" +#include "libfrr.h" +#include "frr_pthread.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_ds.h" +#include "routing_nb.h" + +/* mgmt options, we use GNU getopt library. */ +static const struct option longopts[] = { + {"skip_runas", no_argument, NULL, 'S'}, + {"no_zebra", no_argument, NULL, 'Z'}, + {"socket_size", required_argument, NULL, 's'}, + {0} +}; + +static void mgmt_exit(int); +static void mgmt_vrf_terminate(void); + +/* privileges */ +static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_NET_RAW, + ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN}; + +struct zebra_privs_t mgmt_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0, +}; + +static struct frr_daemon_info mgmtd_di; +char backup_config_file[256]; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received, ignoring"); + + return; + + /* + * This is turned off for the moment. There is all + * sorts of config turned off by mgmt_terminate + * that is not setup properly again in mgmt_reset. + * I see no easy way to do this nor do I see that + * this is a desirable way to reload config + * given the yang work. + */ + /* Terminate all thread. */ + mgmt_terminate(); + + /* + * mgmt_reset(); + */ + zlog_info("MGMTD restarting!"); + + /* + * Reload config file. + * vty_read_config(NULL, mgmtd_di.config_file, config_default); + */ + /* Try to return to normal operation. */ +} + +/* SIGINT handler. */ +static __attribute__((__noreturn__)) void sigint(void) +{ + zlog_notice("Terminating on signal"); + assert(mm->terminating == false); + mm->terminating = true; /* global flag that shutting down */ + + mgmt_terminate(); + + mgmt_exit(0); + + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +static struct frr_signal_t mgmt_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + + +/* + * Try to free up allocations we know about so that diagnostic tools such as + * valgrind are able to better illuminate leaks. + * + * Zebra route removal and protocol teardown are not meant to be done here. + * For example, "retain_mode" may be set. + */ +static __attribute__((__noreturn__)) void mgmt_exit(int status) +{ + /* it only makes sense for this to be called on a clean exit */ + assert(status == 0); + + frr_early_fini(); + + /* stop pthreads (if any) */ + frr_pthread_stop_all(); + + mgmt_vrf_terminate(); + + frr_fini(); + exit(status); +} + +static int mgmt_vrf_new(struct vrf *vrf) +{ + zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int mgmt_vrf_delete(struct vrf *vrf) +{ + zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int mgmt_vrf_enable(struct vrf *vrf) +{ + zlog_debug("VRF Enable: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int mgmt_vrf_disable(struct vrf *vrf) +{ + zlog_debug("VRF Disable: %s(%u)", vrf->name, vrf->vrf_id); + + /* Note: This is a callback, the VRF will be deleted by the caller. */ + return 0; +} + +static int mgmt_vrf_config_write(struct vty *vty) +{ + return 0; +} + +static void mgmt_vrf_init(void) +{ + vrf_init(mgmt_vrf_new, mgmt_vrf_enable, mgmt_vrf_disable, + mgmt_vrf_delete); + vrf_cmd_init(mgmt_vrf_config_write); +} + +static void mgmt_vrf_terminate(void) +{ + vrf_terminate(); +} + +/* + * List of YANG modules to be loaded in the process context of + * MGMTd. + * + * NOTE: In future this will also include the YANG modules of + * all individual Backend clients. + */ +static const struct frr_yang_module_info *const mgmt_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_route_map_info, + &frr_routing_info, &frr_vrf_info, +}; + +FRR_DAEMON_INFO(mgmtd, MGMTD, .vty_port = MGMTD_VTY_PORT, + + .proghelp = "FRR Management Daemon.", + + .signals = mgmt_signals, .n_signals = array_size(mgmt_signals), + + .privs = &mgmt_privs, .yang_modules = mgmt_yang_modules, + .n_yang_modules = array_size(mgmt_yang_modules), +); + +#define DEPRECATED_OPTIONS "" + +/* Main routine of mgmt. Treatment of argument and start mgmt finite + * state machine is handled at here. + */ +int main(int argc, char **argv) +{ + int opt; + int buffer_size = MGMTD_SOCKET_BUF_SIZE; + + frr_preinit(&mgmtd_di, argc, argv); + frr_opt_add( + "s:" DEPRECATED_OPTIONS, longopts, + " -s, --socket_size Set MGMTD peer socket send buffer size\n"); + + /* Command line argument treatment. */ + while (1) { + opt = frr_getopt(argc, argv, 0); + + if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) { + fprintf(stderr, + "The -%c option no longer exists.\nPlease refer to the manual.\n", + opt); + continue; + } + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 's': + buffer_size = atoi(optarg); + break; + default: + frr_help_exit(1); + break; + } + } + + /* MGMTD master init. */ + mgmt_master_init(frr_init(), buffer_size); + + /* VRF Initializations. */ + mgmt_vrf_init(); + + /* MGMTD related initialization. */ + mgmt_init(); + + snprintf(backup_config_file, sizeof(backup_config_file), + "%s/zebra.conf", frr_sysconfdir); + mgmtd_di.backup_config_file = backup_config_file; + + frr_config_fork(); + + frr_run(mm->master); + + /* Not reached. */ + return 0; +} diff --git a/mgmtd/mgmt_memory.c b/mgmtd/mgmt_memory.c new file mode 100644 index 000000000..fb801dde7 --- /dev/null +++ b/mgmtd/mgmt_memory.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mgmt memory type definitions + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mgmt_memory.h" + +/* this file is temporary in nature; definitions should be moved to the + * files they're used in + */ + +DEFINE_MGROUP(MGMTD, "mgmt"); +DEFINE_MTYPE(MGMTD, MGMTD, "MGMTD instance"); diff --git a/mgmtd/mgmt_memory.h b/mgmtd/mgmt_memory.h new file mode 100644 index 000000000..5522c6324 --- /dev/null +++ b/mgmtd/mgmt_memory.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mgmt memory type declarations + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_MEMORY_H +#define _FRR_MGMTD_MEMORY_H + +#include "memory.h" + +DECLARE_MGROUP(MGMTD); +DECLARE_MTYPE(MGMTD); +#endif /* _FRR_MGMTD_MEMORY_H */ diff --git a/mgmtd/mgmt_test_fe b/mgmtd/mgmt_test_fe new file mode 100755 index 000000000..8d6fb8120 --- /dev/null +++ b/mgmtd/mgmt_test_fe @@ -0,0 +1,210 @@ +#! /bin/bash + +# mgmtd/mgmt_test_fe - temporary wrapper script for .libs/mgmt_test_fe +# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2 +# +# The mgmtd/mgmt_test_fe program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.6' + notinst_deplibs=' lib/libfrr.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's ./libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "mgmt_test_fe:mgmtd/mgmt_test_fe:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-2" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "mgmt_test_fe:mgmtd/mgmt_test_fe:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "mgmt_test_fe:mgmtd/mgmt_test_fe:$LINENO: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program='mgmt_test_fe' + progdir="$thisdir/.libs" + + + if test -f "$progdir/$program"; then + # Add our own library path to LD_LIBRARY_PATH + LD_LIBRARY_PATH="/root/upstream_p1/lib/.libs:$LD_LIBRARY_PATH" + + # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH + # The second colon is a workaround for a bug in BeOS R4 sed + LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | sed 's/::*$//'` + + export LD_LIBRARY_PATH + + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c new file mode 100644 index 000000000..3070b754f --- /dev/null +++ b/mgmtd/mgmt_vty.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD VTY Interface + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> + +#include "command.h" +#include "json.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_ds.h" + +#ifndef VTYSH_EXTRACT_PL +#include "mgmtd/mgmt_vty_clippy.c" +#endif + +DEFPY(show_mgmt_ds, + show_mgmt_ds_cmd, + "show mgmt datastore [all|candidate|operational|running]$dsname", + SHOW_STR + MGMTD_STR + MGMTD_DS_STR + "All datastores (default)\n" + "Candidate datastore\n" + "Operational datastore\n" + "Running datastore\n") +{ + struct mgmt_ds_ctx *ds_ctx; + + if (!dsname || dsname[0] == 'a') { + mgmt_ds_status_write(vty); + return CMD_SUCCESS; + } + ds_ctx = mgmt_ds_get_ctx_by_id(mm, mgmt_ds_name2id(dsname)); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access %s datastore!\n", dsname); + return CMD_ERR_NO_MATCH; + } + mgmt_ds_status_write_one(vty, ds_ctx); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_dump_data, + show_mgmt_dump_data_cmd, + "show mgmt datastore-contents WORD$dsname [xpath WORD$path] [file WORD$filepath] <json|xml>$fmt", + SHOW_STR + MGMTD_STR + "Get Datastore contents from a specific datastore\n" + "<candidate | running | operational>\n" + "XPath expression specifying the YANG data path\n" + "XPath string\n" + "Dump the contents to a file\n" + "Full path of the file\n" + "json|xml\n") +{ + enum mgmt_datastore_id datastore = MGMTD_DS_CANDIDATE; + struct mgmt_ds_ctx *ds_ctx; + LYD_FORMAT format = fmt[0] == 'j' ? LYD_JSON : LYD_XML; + FILE *f = NULL; + + datastore = mgmt_ds_name2id(dsname); + + if (datastore == MGMTD_DS_NONE) { + vty_out(vty, + "DS Name %s does not matches any existing datastore\n", + dsname); + return CMD_SUCCESS; + } + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access datastore!\n"); + return CMD_ERR_NO_MATCH; + } + + if (filepath) { + f = fopen(filepath, "w"); + if (!f) { + vty_out(vty, + "Could not open file pointed by filepath %s\n", + filepath); + return CMD_SUCCESS; + } + } + + mgmt_ds_dump_tree(vty, ds_ctx, path, f, format); + + if (f) + fclose(f); + return CMD_SUCCESS; +} + +DEFPY(mgmt_load_config, + mgmt_load_config_cmd, + "mgmt load-config file WORD$filepath <merge|replace>", + MGMTD_STR + "Load configuration onto Candidate Datastore\n" + "Read the configuration from a file\n" + "Full path of the file\n" + "Merge configuration with contents of Candidate Datastore\n" + "Replace the existing contents of Candidate datastore\n") +{ + bool merge = false; + int idx_merge = 4; + int ret; + struct mgmt_ds_ctx *ds_ctx; + + if (access(filepath, F_OK) == -1) { + vty_out(vty, "ERROR: File %s : %s\n", filepath, + strerror(errno)); + return CMD_ERR_NO_FILE; + } + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_CANDIDATE); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access Candidate datastore!\n"); + return CMD_ERR_NO_MATCH; + } + + if (strncmp(argv[idx_merge]->arg, "merge", sizeof("merge")) == 0) + merge = true; + else if (strncmp(argv[idx_merge]->arg, "replace", sizeof("replace")) + == 0) + merge = false; + else { + vty_out(vty, "Chosen option: %s not valid\n", + argv[idx_merge]->arg); + return CMD_SUCCESS; + } + + ret = mgmt_ds_load_config_from_file(ds_ctx, filepath, merge); + if (ret != 0) + vty_out(vty, "Error with parsing the file with error code %d\n", + ret); + return CMD_SUCCESS; +} + +DEFPY(mgmt_save_config, + mgmt_save_config_cmd, + "mgmt save-config datastore WORD$dsname file WORD$filepath", + MGMTD_STR + "Save configuration from datastore\n" + "Datastore keyword\n" + "<candidate|running>\n" + "Write the configuration to a file\n" + "Full path of the file\n") +{ + struct mgmt_ds_ctx *ds_ctx; + enum mgmt_datastore_id datastore; + FILE *f; + + datastore = mgmt_ds_name2id(dsname); + + if (datastore == MGMTD_DS_NONE) { + vty_out(vty, + "DS Name %s does not matches any existing datastore\n", + dsname); + return CMD_SUCCESS; + } + + if (datastore != MGMTD_DS_CANDIDATE && datastore != MGMTD_DS_RUNNING) { + vty_out(vty, "DS Name %s is not a configuration datastore\n", + dsname); + return CMD_SUCCESS; + } + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access the '%s' datastore!\n", + dsname); + return CMD_ERR_NO_MATCH; + } + + if (!filepath) { + vty_out(vty, "ERROR: No file path mentioned!\n"); + return CMD_ERR_NO_MATCH; + } + + f = fopen(filepath, "w"); + if (!f) { + vty_out(vty, "Could not open file pointed by filepath %s\n", + filepath); + return CMD_SUCCESS; + } + + mgmt_ds_dump_tree(vty, ds_ctx, "/", f, LYD_JSON); + + fclose(f); + + return CMD_SUCCESS; +} + +static int config_write_mgmt_debug(struct vty *vty) +{ + int n = mgmt_debug_be + mgmt_debug_fe + mgmt_debug_ds + mgmt_debug_txn; + if (!n) + return 0; + if (n == 4) { + vty_out(vty, "debug mgmt all\n"); + return 0; + } + + vty_out(vty, "debug mgmt"); + if (mgmt_debug_be) + vty_out(vty, " backend"); + if (mgmt_debug_ds) + vty_out(vty, " datastore"); + if (mgmt_debug_fe) + vty_out(vty, " frontend"); + if (mgmt_debug_txn) + vty_out(vty, " transaction"); + + vty_out(vty, "\n"); + + return 0; +} +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_mgmt_debug, +}; + +DEFPY(debug_mgmt, + debug_mgmt_cmd, + "[no$no] debug mgmt <all$all|{backend$be|datastore$ds|frontend$fe|transaction$txn}>", + NO_STR + DEBUG_STR + MGMTD_STR + "All debug\n" + "Back-end debug\n" + "Datastore debug\n" + "Front-end debug\n" + "Transaction debug\n") +{ + bool set = !no; + if (all) + be = fe = ds = txn = set ? all : NULL; + + if (be) + mgmt_debug_be = set; + if (ds) + mgmt_debug_ds = set; + if (fe) + mgmt_debug_fe = set; + if (txn) + mgmt_debug_txn = set; + + return CMD_SUCCESS; +} + +void mgmt_vty_init(void) +{ + install_node(&debug_node); + + install_element(VIEW_NODE, &show_mgmt_ds_cmd); + install_element(VIEW_NODE, &show_mgmt_dump_data_cmd); + + install_element(CONFIG_NODE, &mgmt_load_config_cmd); + install_element(CONFIG_NODE, &mgmt_save_config_cmd); + + install_element(VIEW_NODE, &debug_mgmt_cmd); + install_element(CONFIG_NODE, &debug_mgmt_cmd); + + /* + * TODO: Register and handlers for auto-completion here (if any). + */ +} diff --git a/mgmtd/mgmt_vty.c.safe b/mgmtd/mgmt_vty.c.safe new file mode 100644 index 000000000..c43485c92 --- /dev/null +++ b/mgmtd/mgmt_vty.c.safe @@ -0,0 +1,506 @@ +/* + * MGMTD VTY Interface + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "command.h" +#include "json.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_be_server.h" +#include "mgmtd/mgmt_be_adapter.h" +#include "mgmtd/mgmt_fe_server.h" +#include "mgmtd/mgmt_fe_adapter.h" +#include "mgmtd/mgmt_ds.h" +#include "mgmtd/mgmt_history.h" + +#include "mgmtd/mgmt_vty_clippy.c" + +DEFPY(show_mgmt_be_adapter, + show_mgmt_be_adapter_cmd, + "show mgmt backend-adapter all", + SHOW_STR + MGMTD_STR + MGMTD_BE_ADAPTER_STR + "Display all Backend Adapters\n") +{ + mgmt_be_adapter_status_write(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_be_xpath_reg, + show_mgmt_be_xpath_reg_cmd, + "show mgmt backend-yang-xpath-registry", + SHOW_STR + MGMTD_STR + "Backend Adapter YANG Xpath Registry\n") +{ + mgmt_be_xpath_register_write(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_fe_adapter, + show_mgmt_fe_adapter_cmd, + "show mgmt frontend-adapter all", + SHOW_STR MGMTD_STR MGMTD_FE_ADAPTER_STR "Display all Frontend Adapters\n") +{ + mgmt_fe_adapter_status_write(vty, false); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_fe_adapter_detail, show_mgmt_fe_adapter_detail_cmd, + "show mgmt frontend-adapter all detail", + SHOW_STR MGMTD_STR MGMTD_FE_ADAPTER_STR + "Display all Frontend Adapters\n" + "Details of commit stats\n") +{ + mgmt_fe_adapter_status_write(vty, true); + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN(mgmt_performance_measurement, + mgmt_performance_measurement_cmd, + "[no] mgmt performance-measurement", + NO_STR + MGMTD_STR + "Enable performance measurement\n") +{ + if (no) + mgmt_fe_adapter_perf_measurement(vty, false); + else + mgmt_fe_adapter_perf_measurement(vty, true); + + return CMD_SUCCESS; +} + +DEFPY(mgmt_reset_performance_stats, + mgmt_reset_performance_stats_cmd, + "mgmt reset-statistics", + MGMTD_STR + "Reset the Performance measurement statistics\n") +{ + mgmt_fe_adapter_reset_perf_stats(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_txn, + show_mgmt_txn_cmd, + "show mgmt transaction all", + SHOW_STR + MGMTD_STR + MGMTD_TXN_STR + "Display all Transactions\n") +{ + mgmt_txn_status_write(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_ds, + show_mgmt_ds_cmd, + "show mgmt datastore [all|candidate|operational|running]$dsname", + SHOW_STR + MGMTD_STR + MGMTD_DS_STR + "All datastores (default)\n" + "Candidate datastore\n" + "Operational datastore\n" + "Running datastore\n") +{ + struct mgmt_ds_ctx *ds_ctx; + + if (!dsname || dsname[0] == 'a') { + mgmt_ds_status_write(vty); + return CMD_SUCCESS; + } + ds_ctx = mgmt_ds_get_ctx_by_id(mm, mgmt_ds_name2id(dsname)); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access %s datastore!\n", dsname); + return CMD_ERR_NO_MATCH; + } + mgmt_ds_status_write_one(vty, ds_ctx); + + return CMD_SUCCESS; +} + +DEFPY(mgmt_commit, + mgmt_commit_cmd, + "mgmt commit <check|apply|abort>$type", + MGMTD_STR + "Commit action\n" + "Validate the set of config commands\n" + "Validate and apply the set of config commands\n" + "Abort and drop the set of config commands recently added\n") +{ + bool validate_only = type[0] == 'c'; + bool abort = type[1] == 'b'; + + if (vty_mgmt_send_commit_config(vty, validate_only, abort) != 0) + return CMD_WARNING_CONFIG_FAILED; + return CMD_SUCCESS; +} + +DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd, + "mgmt set-config WORD$path VALUE", + MGMTD_STR + "Set configuration data\n" + "XPath expression specifying the YANG data path\n" + "Value of the data to set\n") +{ + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = value; + vty->cfg_changes[0].operation = NB_OP_CREATE; + vty->num_cfg_changes = 1; + + vty->no_implicit_commit = true; + vty_mgmt_send_config_data(vty); + vty->no_implicit_commit = false; + return CMD_SUCCESS; +} + +DEFPY(mgmt_delete_config_data, mgmt_delete_config_data_cmd, + "mgmt delete-config WORD$path", + MGMTD_STR + "Delete configuration data\n" + "XPath expression specifying the YANG data path\n") +{ + + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = NULL; + vty->cfg_changes[0].operation = NB_OP_DESTROY; + vty->num_cfg_changes = 1; + + vty->no_implicit_commit = true; + vty_mgmt_send_config_data(vty); + vty->no_implicit_commit = false; + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd, + "show mgmt get-config [candidate|operational|running]$dsname WORD$path", + SHOW_STR MGMTD_STR + "Get configuration data from a specific configuration datastore\n" + "Candidate datastore (default)\n" + "Operational datastore\n" + "Running datastore\n" + "XPath expression specifying the YANG data path\n") +{ + const char *xpath_list[VTY_MAXCFGCHANGES] = {0}; + Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE; + + if (dsname) + datastore = mgmt_ds_name2id(dsname); + + xpath_list[0] = path; + vty_mgmt_send_get_config(vty, datastore, xpath_list, 1); + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd, + "show mgmt get-data [candidate|operational|running]$dsname WORD$path", + SHOW_STR MGMTD_STR + "Get data from a specific datastore\n" + "Candidate datastore\n" + "Operational datastore (default)\n" + "Running datastore\n" + "XPath expression specifying the YANG data path\n") +{ + const char *xpath_list[VTY_MAXCFGCHANGES] = {0}; + Mgmtd__DatastoreId datastore = MGMTD_DS_OPERATIONAL; + + if (dsname) + datastore = mgmt_ds_name2id(dsname); + + xpath_list[0] = path; + vty_mgmt_send_get_data(vty, datastore, xpath_list, 1); + 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", + SHOW_STR + MGMTD_STR + "Get Datastore contents from a specific datastore\n" + "Candidate datastore (default)\n" + "Operational datastore\n" + "Running datastore\n" + "XPath expression specifying the YANG data path\n" + "XPath string\n" + "Dump the contents to a file\n" + "Full path of the file\n" + "json output\n" + "xml output\n") +{ + struct mgmt_ds_ctx *ds_ctx; + Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE; + LYD_FORMAT format = fmt[0] == 'j' ? LYD_JSON : LYD_XML; + FILE *f = NULL; + + if (datastore) + datastore = mgmt_ds_name2id(dsname); + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access datastore!\n"); + return CMD_ERR_NO_MATCH; + } + + if (filepath) { + f = fopen(filepath, "w"); + if (!f) { + vty_out(vty, + "Could not open file pointed by filepath %s\n", + filepath); + return CMD_SUCCESS; + } + } + + mgmt_ds_dump_tree(vty, ds_ctx, path, f, format); + + if (f) + fclose(f); + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_map_xpath, + show_mgmt_map_xpath_cmd, + "show mgmt yang-xpath-subscription WORD$path", + SHOW_STR + MGMTD_STR + "Get YANG Backend Subscription\n" + "XPath expression specifying the YANG data path\n") +{ + mgmt_be_xpath_subscr_info_write(vty, path); + return CMD_SUCCESS; +} + +DEFPY(mgmt_load_config, + mgmt_load_config_cmd, + "mgmt load-config WORD$filepath <merge|replace>$type", + MGMTD_STR + "Load configuration onto Candidate Datastore\n" + "Full path of the file\n" + "Merge configuration with contents of Candidate Datastore\n" + "Replace the existing contents of Candidate datastore\n") +{ + bool merge = type[0] == 'm' ? true : false; + struct mgmt_ds_ctx *ds_ctx; + int ret; + + if (access(filepath, F_OK) == -1) { + vty_out(vty, "ERROR: File %s : %s\n", filepath, + strerror(errno)); + return CMD_ERR_NO_FILE; + } + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_CANDIDATE); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access Candidate datastore!\n"); + return CMD_ERR_NO_MATCH; + } + + ret = mgmt_ds_load_config_from_file(ds_ctx, filepath, merge); + if (ret != 0) + vty_out(vty, "Error with parsing the file with error code %d\n", + ret); + return CMD_SUCCESS; +} + +DEFPY(mgmt_save_config, + mgmt_save_config_cmd, + "mgmt save-config <candidate|running>$dsname WORD$filepath", + MGMTD_STR + "Save configuration from datastore\n" + "Candidate datastore\n" + "Running datastore\n" + "Full path of the file\n") +{ + Mgmtd__DatastoreId datastore = mgmt_ds_name2id(dsname); + struct mgmt_ds_ctx *ds_ctx; + FILE *f; + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access the '%s' datastore!\n", + dsname); + return CMD_ERR_NO_MATCH; + } + + if (!filepath) { + vty_out(vty, "ERROR: No file path mentioned!\n"); + return CMD_ERR_NO_MATCH; + } + + f = fopen(filepath, "w"); + if (!f) { + vty_out(vty, "Could not open file pointed by filepath %s\n", + filepath); + return CMD_SUCCESS; + } + + mgmt_ds_dump_tree(vty, ds_ctx, "/", f, LYD_JSON); + + fclose(f); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_cmt_hist, + show_mgmt_cmt_hist_cmd, + "show mgmt commit-history", + SHOW_STR + MGMTD_STR + "Show commit history\n") +{ + show_mgmt_cmt_history(vty); + return CMD_SUCCESS; +} + +DEFPY(mgmt_rollback, + mgmt_rollback_cmd, + "mgmt rollback <commit-id WORD$commit | last [(1-10)]$last>", + MGMTD_STR + "Rollback commits\n" + "Rollback to commit ID\n" + "Commit-ID\n" + "Rollbak n commits\n" + "Number of commits\n") +{ + if (commit) + mgmt_history_rollback_by_id(vty, commit); + else + mgmt_history_rollback_n(vty, last); + + return CMD_SUCCESS; +} + +static int config_write_mgmt_debug(struct vty *vty); +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_mgmt_debug, +}; + +static int config_write_mgmt_debug(struct vty *vty) +{ + int n = mgmt_debug_be + mgmt_debug_fe + mgmt_debug_ds + mgmt_debug_txn; + if (!n) + return 0; + if (n == 4) { + vty_out(vty, "debug mgmt all\n"); + return 0; + } + + vty_out(vty, "debug mgmt"); + if (mgmt_debug_be) + vty_out(vty, " backend"); + if (mgmt_debug_ds) + vty_out(vty, " datastore"); + if (mgmt_debug_fe) + vty_out(vty, " frontend"); + if (mgmt_debug_txn) + vty_out(vty, " transaction"); + + vty_out(vty, "\n"); + + return 0; +} + +DEFPY(debug_mgmt, + debug_mgmt_cmd, + "[no$no] debug mgmt <all$all|{backend$be|datastore$ds|frontend$fe|transaction$txn}>", + NO_STR + DEBUG_STR + MGMTD_STR + "All debug\n" + "Back-end debug\n" + "Datastore debug\n" + "Front-end debug\n" + "Transaction debug\n") +{ + bool set = !no; + if (all) + be = fe = ds = txn = set ? all : NULL; + + if (be) + mgmt_debug_be = set; + if (ds) + mgmt_debug_ds = set; + if (fe) + mgmt_debug_fe = set; + if (txn) + mgmt_debug_txn = set; + + return CMD_SUCCESS; +} + +void mgmt_vty_init(void) +{ + /* + * Initialize command handling from VTYSH connection. + * Call command initialization routines defined by + * backend components that are moved to new MGMTD infra + * here one by one. + */ +#if HAVE_STATICD + extern void static_vty_init(void); + static_vty_init(); +#endif + + install_node(&debug_node); + + install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd); + install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd); + install_element(VIEW_NODE, &show_mgmt_fe_adapter_cmd); + install_element(VIEW_NODE, &show_mgmt_fe_adapter_detail_cmd); + install_element(VIEW_NODE, &show_mgmt_txn_cmd); + 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_dump_data_cmd); + install_element(VIEW_NODE, &show_mgmt_map_xpath_cmd); + install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd); + + install_element(CONFIG_NODE, &mgmt_commit_cmd); + install_element(CONFIG_NODE, &mgmt_set_config_data_cmd); + install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd); + install_element(CONFIG_NODE, &mgmt_load_config_cmd); + install_element(CONFIG_NODE, &mgmt_save_config_cmd); + install_element(CONFIG_NODE, &mgmt_rollback_cmd); + + install_element(VIEW_NODE, &debug_mgmt_cmd); + install_element(CONFIG_NODE, &debug_mgmt_cmd); + + /* Enable view */ + install_element(ENABLE_NODE, &mgmt_performance_measurement_cmd); + install_element(ENABLE_NODE, &mgmt_reset_performance_stats_cmd); + + /* + * TODO: Register and handlers for auto-completion here. + */ +} diff --git a/mgmtd/subdir.am b/mgmtd/subdir.am new file mode 100644 index 000000000..540ee07c3 --- /dev/null +++ b/mgmtd/subdir.am @@ -0,0 +1,42 @@ +# +# mgmtd -- Mangagement Daemon +# + +# dist_examples_DATA += \ + # end + +vtysh_daemons += mgmtd + +# man8 += $(MANBUILD)/frr-mgmtd.8 +# endif + +clippy_scan += \ + mgmtd/mgmt_vty.c \ + # end + +noinst_LIBRARIES += mgmtd/libmgmtd.a +mgmtd_libmgmtd_a_SOURCES = \ + mgmtd/mgmt.c \ + mgmtd/mgmt_ds.c \ + mgmtd/mgmt_memory.c \ + mgmtd/mgmt_vty.c \ + # end + +mgmtdheaderdir = $(pkgincludedir)/mgmtd +mgmtdheader_HEADERS = \ + mgmtd/mgmt_defines.h \ + # end + +noinst_HEADERS += \ + mgmtd/mgmt.h \ + mgmtd/mgmt_ds.h \ + mgmtd/mgmt_memory.h \ + # end + +sbin_PROGRAMS += mgmtd/mgmtd + +mgmtd_mgmtd_SOURCES = \ + mgmtd/mgmt_main.c \ + # end +mgmtd_mgmtd_CFLAGS = $(AM_CFLAGS) -I ./ +mgmtd_mgmtd_LDADD = mgmtd/libmgmtd.a lib/libfrr.la $(LIBCAP) $(LIBM) $(LIBYANG_LIBS) $(UST_LIBS) |