summaryrefslogtreecommitdiffstats
path: root/mgmtd
diff options
context:
space:
mode:
authorChristian Hopps <chopps@labn.net>2023-03-08 23:22:09 +0100
committerChristian Hopps <chopps@labn.net>2023-03-22 03:08:32 +0100
commit1c84efe4fa8585df58a9b53459f94c47934f0786 (patch)
tree21609ad9048500a6b23c38ce373506d5d6aa6ac3 /mgmtd
parentMerge pull request #13073 from donaldsharp/static_use_after_free (diff)
downloadfrr-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/.gitignore1
-rw-r--r--mgmtd/Makefile10
-rw-r--r--mgmtd/mgmt.c49
-rw-r--r--mgmtd/mgmt.h94
-rw-r--r--mgmtd/mgmt_defines.h20
-rw-r--r--mgmtd/mgmt_ds.c643
-rw-r--r--mgmtd/mgmt_ds.h402
-rw-r--r--mgmtd/mgmt_main.c271
-rw-r--r--mgmtd/mgmt_memory.c21
-rw-r--r--mgmtd/mgmt_memory.h16
-rwxr-xr-xmgmtd/mgmt_test_fe210
-rw-r--r--mgmtd/mgmt_vty.c272
-rw-r--r--mgmtd/mgmt_vty.c.safe506
-rw-r--r--mgmtd/subdir.am42
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)