]> git.proxmox.com Git - mirror_frr.git/commitdiff
mgmtd: Bringup MGMTD daemon and datastore module support
authorChristian Hopps <chopps@labn.net>
Wed, 8 Mar 2023 22:22:09 +0000 (17:22 -0500)
committerChristian Hopps <chopps@labn.net>
Wed, 22 Mar 2023 02:08:32 +0000 (22:08 -0400)
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>
27 files changed:
Makefile.am
configure.ac
lib/command.h
lib/northbound.c
lib/northbound.h
mgmtd/.gitignore [new file with mode: 0644]
mgmtd/Makefile [new file with mode: 0644]
mgmtd/mgmt.c [new file with mode: 0644]
mgmtd/mgmt.h [new file with mode: 0644]
mgmtd/mgmt_defines.h [new file with mode: 0644]
mgmtd/mgmt_ds.c [new file with mode: 0644]
mgmtd/mgmt_ds.h [new file with mode: 0644]
mgmtd/mgmt_main.c [new file with mode: 0644]
mgmtd/mgmt_memory.c [new file with mode: 0644]
mgmtd/mgmt_memory.h [new file with mode: 0644]
mgmtd/mgmt_test_fe [new file with mode: 0755]
mgmtd/mgmt_vty.c [new file with mode: 0644]
mgmtd/mgmt_vty.c.safe [new file with mode: 0644]
mgmtd/subdir.am [new file with mode: 0644]
pkgsrc/mgmtd.sh.in [new file with mode: 0644]
qpb/subdir.am
redhat/frr.spec.in
tools/etc/frr/daemons
tools/frr.in
tools/frrcommon.sh.in
vtysh/vtysh.c
vtysh/vtysh.h

index 44d2ab8e72b80d5533430df61f62ebb726519528..e70e65d333ef750e3547c8016a4610c03faddaf2 100644 (file)
@@ -155,6 +155,24 @@ $(AUTOMAKE_DUMMY)install-moduleLTLIBRARIES: install-libLTLIBRARIES
 $(AUTOMAKE_DUMMY)install-binPROGRAMS: install-libLTLIBRARIES
 $(AUTOMAKE_DUMMY)install-sbinPROGRAMS: install-libLTLIBRARIES
 
+# Include default rules to compile protobuf message sources
+SUFFIXES += .proto .pb-c.c .pb-c.h
+
+# Rules
+
+AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V))
+am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY))
+am__v_PROTOC_C_0 = @echo "  PROTOC_C" $@;
+am__v_PROTOC_C_1 =
+
+.proto.pb-c.c:
+       $(AM_V_PROTOC_C)$(PROTOC_C) -I$(top_srcdir) --c_out=$(top_builddir) $^
+       $(AM_V_GEN)$(SED) -i -e '1i\
+       #include "config.h"' $@
+
+.pb-c.c.pb-c.h:
+       @echo "  GEN     " $@
+
 include doc/subdir.am
 include doc/user/subdir.am
 include doc/manpages/subdir.am
@@ -169,6 +187,8 @@ include fpm/subdir.am
 include grpc/subdir.am
 include tools/subdir.am
 
+include mgmtd/subdir.am
+
 include bgpd/subdir.am
 include bgpd/rfp-example/librfp/subdir.am
 include bgpd/rfp-example/rfptest/subdir.am
@@ -207,6 +227,7 @@ rc_SCRIPTS = \
        pkgsrc/ripd.sh \
        pkgsrc/ripngd.sh \
        pkgsrc/zebra.sh \
+       pkgsrc/mgmtd.sh \
        # end
 endif
 
@@ -244,6 +265,7 @@ EXTRA_DIST += \
        snapcraft/helpers \
        snapcraft/snap \
        babeld/Makefile \
+       mgmtd/Makefile \
        bgpd/Makefile \
        bgpd/rfp-example/librfp/Makefile \
        bgpd/rfp-example/rfptest/Makefile \
@@ -321,7 +343,7 @@ redistclean:
        $(MAKE) distclean CONFIG_CLEAN_FILES="$(filter-out $(EXTRA_DIST), $(CONFIG_CLEAN_FILES))"
 
 indent:
-       tools/indent.py `find sharpd bgpd eigrpd include isisd lib nhrpd ospf6d ospfd pimd qpb ripd vtysh zebra -name '*.[ch]' | grep -v include/linux`
+       tools/indent.py `find sharpd bgpd mgmtd eigrpd include isisd lib nhrpd ospf6d ospfd pimd qpb ripd vtysh zebra -name '*.[ch]' | grep -v include/linux`
 
 if HAVE_GCOV
 
index 495c89c0d6104ae21ca8b9f32379468d1a283eae..7d8b73ddd637a35abab82e8a05a6d104d0609d04 100644 (file)
@@ -616,6 +616,8 @@ AC_ARG_ENABLE([zebra],
   AS_HELP_STRING([--disable-zebra], [do not build zebra daemon]))
 AC_ARG_ENABLE([bgpd],
   AS_HELP_STRING([--disable-bgpd], [do not build bgpd]))
+AC_ARG_ENABLE([mgmtd],
+  AS_HELP_STRING([--disable-mgmtd], [do not build mgmtd]))
 AC_ARG_ENABLE([ripd],
   AS_HELP_STRING([--disable-ripd], [do not build ripd]))
 AC_ARG_ENABLE([ripngd],
@@ -890,10 +892,6 @@ if test "$enable_oldvpn_commands" = "yes"; then
    AC_DEFINE([KEEP_OLD_VPN_COMMANDS], [1], [Define for compiling with old vpn commands])
 fi
 
-#
-# End of logic for protobuf support.
-#
-
 AC_MSG_CHECKING([if zebra should be configurable to send Route Advertisements])
 if test "$enable_rtadv" != "no"; then
   AC_MSG_RESULT([yes])
@@ -1338,21 +1336,21 @@ dnl ##########################################################################
 # Logic for protobuf support.
 #
 PROTO3=false
-if test "$enable_protobuf" = "yes"; then
-  # Check for protoc & protoc-c
-
-  # protoc is not required, it's only for a "be nice" helper target
-  AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false])
+# Enable Protobuf by default at all times.
+# Check for protoc & protoc-c
+# protoc is not required, it's only for a "be nice" helper target
+AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false])
 
-  AC_CHECK_PROGS([PROTOC_C], [protoc-c], [/bin/false])
-  if test "$PROTOC_C" = "/bin/false"; then
-    AC_MSG_FAILURE([protobuf requested but protoc-c not found.  Install protobuf-c.])
-  fi
+AC_CHECK_PROGS([PROTOC_C], [protoc-c], [/bin/false])
+if test "$PROTOC_C" = "/bin/false"; then
+  AC_MSG_FAILURE([protobuf requested but protoc-c not found.  Install protobuf-c.])
+fi
 
-  PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 0.14],, [
-    AC_MSG_FAILURE([protobuf requested but libprotobuf-c not found.  Install protobuf-c.])
-  ])
+PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 1.1.0],, [
+  AC_MSG_FAILURE([minimum version (1.1.0) of libprotobuf-c not found.  Install minimum required version of protobuf-c.])
+])
 
+if test "$enable_protobuf3" = "yes"; then
   PROTO3=true
   AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h],
                   [AC_CHECK_DECLS(PROTOBUF_C_LABEL_NONE,
@@ -1360,11 +1358,14 @@ if test "$enable_protobuf" = "yes"; then
                                             [1], [Have Protobuf version 3]),
                                   [PROTO3=false],
                                   [#include <google/protobuf-c/protobuf-c.h>])],
-                  [PROTO3=false && AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found.  Install protobuf-c.])])
-
-  AC_DEFINE([HAVE_PROTOBUF], [1], [protobuf])
+                  [PROTO3=false && AC_MSG_FAILURE([protobuf3 requested but protobuf-c.h not found.  Install protobuf-c.])])
 fi
 
+AC_DEFINE([HAVE_PROTOBUF], [1], [protobuf])
+#
+# End of logic for protobuf support.
+#
+
 
 dnl ---------------------
 dnl Integrated VTY option
@@ -1728,6 +1729,11 @@ AS_IF([test "$enable_bgpd" != "no"], [
   AC_DEFINE([HAVE_BGPD], [1], [bgpd])
 ])
 
+AS_IF([test "$enable_mgmtd" != "no"], [
+
+  AC_DEFINE([HAVE_MGMTD], [1], [mgmtd])
+])
+
 AS_IF([test "$enable_ripd" != "no"], [
   AC_DEFINE([HAVE_RIPD], [1], [ripd])
 ])
@@ -2658,6 +2664,8 @@ AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control
 AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket])
 AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket])
 AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information])
+AC_DEFINE_UNQUOTED([MGMTD_FE_SERVER_PATH], ["$frr_statedir/mgmtd_fe.sock"], [mgmtd frontend server socket])
+AC_DEFINE_UNQUOTED([MGMTD_BE_SERVER_PATH], ["$frr_statedir/mgmtd_be.sock"], [mgmtd backend server socket])
 AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information])
 AC_DEFINE_UNQUOTED([ISISD_RESTART], ["$frr_statedir%s/isid-restart.json"], [isisd restart information])
 AC_DEFINE_UNQUOTED([OSPF6_AUTH_SEQ_NUM_FILE], ["$frr_statedir/ospf6d-at-seq-no.dat"], [ospf6d AT Sequence number information])
@@ -2716,7 +2724,7 @@ AM_CONDITIONAL([RPKI], [test "$RPKI" = "true"])
 AM_CONDITIONAL([SNMP], [test "$SNMP_METHOD" = "agentx"])
 AM_CONDITIONAL([IRDP], [$IRDP])
 AM_CONDITIONAL([FPM], [test "$enable_fpm" = "yes"])
-AM_CONDITIONAL([HAVE_PROTOBUF], [test "$enable_protobuf" = "yes"])
+AM_CONDITIONAL([HAVE_PROTOBUF], [test "$enable_protobuf" != "no"])
 AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3])
 
 dnl PCEP plugin
@@ -2733,6 +2741,7 @@ dnl daemons
 AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"])
 AM_CONDITIONAL([ZEBRA], [test "$enable_zebra" != "no"])
 AM_CONDITIONAL([BGPD], [test "$enable_bgpd" != "no"])
+AM_CONDITIONAL([MGMTD], [test "$enable_mgmtd" != "no"])
 AM_CONDITIONAL([RIPD], [test "$enable_ripd" != "no"])
 AM_CONDITIONAL([OSPFD], [test "$enable_ospfd" != "no"])
 AM_CONDITIONAL([LDPD], [test "$enable_ldpd" != "no"])
@@ -2770,7 +2779,7 @@ AC_CONFIG_FILES([
          alpine/APKBUILD
          snapcraft/snapcraft.yaml
          lib/version.h
-         tests/lib/cli/test_cli.refout
+         tests/lib/cli/test_cli.refout pkgsrc/mgmtd.sh
          pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh
          pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh
          pkgsrc/eigrpd.sh])
index 6538e565880923d89efb6200a535c1077a2d55d4..8856f9f09f2adf1cf4821c0b0f63fb48ae304904 100644 (file)
@@ -429,6 +429,11 @@ struct cmd_node {
 #define SHARP_STR "Sharp Routing Protocol\n"
 #define OSPF_GR_STR                                                            \
        "OSPF non-stop forwarding (NSF) also known as OSPF Graceful Restart\n"
+#define MGMTD_STR "Management Daemon (MGMTD) information\n"
+#define MGMTD_BE_ADAPTER_STR "MGMTD Backend Adapter information\n"
+#define MGMTD_FE_ADAPTER_STR "MGMTD Frontend Adapter information\n"
+#define MGMTD_TXN_STR "MGMTD Transaction information\n"
+#define MGMTD_DS_STR "MGMTD Datastore information\n"
 
 #define CMD_VNI_RANGE "(1-16777215)"
 #define CONF_BACKUP_EXT ".sav"
index 6f2c522a294ff016074f7ce1dafed8fd9c2e5316..0d4c3f0f0da7e1a318cff93bc811abb03645c313 100644 (file)
@@ -2389,6 +2389,8 @@ const char *nb_client_name(enum nb_client client)
                return "gRPC";
        case NB_CLIENT_PCEP:
                return "Pcep";
+       case NB_CLIENT_MGMTD_SERVER:
+               return "MGMTD Server";
        case NB_CLIENT_NONE:
                return "None";
        }
index 152810b3a98221db90a2b10eb0a530fad06d876d..064e55cf771425bdd6bfaa3817f05409fdc3f1ef 100644 (file)
@@ -613,6 +613,7 @@ enum nb_client {
        NB_CLIENT_SYSREPO,
        NB_CLIENT_GRPC,
        NB_CLIENT_PCEP,
+       NB_CLIENT_MGMTD_SERVER,
 };
 
 /* Northbound context. */
diff --git a/mgmtd/.gitignore b/mgmtd/.gitignore
new file mode 100644 (file)
index 0000000..7ce107e
--- /dev/null
@@ -0,0 +1 @@
+mgmtd
diff --git a/mgmtd/Makefile b/mgmtd/Makefile
new file mode 100644 (file)
index 0000000..d69ec5f
--- /dev/null
@@ -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 (file)
index 0000000..1901bde
--- /dev/null
@@ -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 (file)
index 0000000..8d6425f
--- /dev/null
@@ -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 (file)
index 0000000..7ff803b
--- /dev/null
@@ -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 (file)
index 0000000..85ff1da
--- /dev/null
@@ -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 (file)
index 0000000..61fb7dd
--- /dev/null
@@ -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 (file)
index 0000000..050367e
--- /dev/null
@@ -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 (file)
index 0000000..fb801dd
--- /dev/null
@@ -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 (file)
index 0000000..5522c63
--- /dev/null
@@ -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 (executable)
index 0000000..8d6fb81
--- /dev/null
@@ -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 (file)
index 0000000..3070b75
--- /dev/null
@@ -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 (file)
index 0000000..c43485c
--- /dev/null
@@ -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 (file)
index 0000000..540ee07
--- /dev/null
@@ -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)
diff --git a/pkgsrc/mgmtd.sh.in b/pkgsrc/mgmtd.sh.in
new file mode 100644 (file)
index 0000000..fb57c0a
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# mgmtd is part of the quagga routing beast
+#
+# PROVIDE: mgmtd
+# REQUIRE: none
+##
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin
+export PATH
+
+if [ -f /etc/rc.subr ]
+then
+       . /etc/rc.subr
+fi
+
+name="mgmtd"
+rcvar=$name
+required_files="@sysconfdir@/${name}.conf"
+command="@prefix@/sbin/${name}"
+command_args="-d"
+
+start_precmd="zebra_precmd"
+socket_dir=@localstatedir@
+pidfile="${socket_dir}/${name}.pid"
+
+zebra_precmd()
+{
+    rc_flags="$(
+       set -- $rc_flags
+       while [ $# -ne 0 ]; do
+           if [ X"$1" = X-P -o X"$1" = X-A ]; then
+               break
+           fi
+           shift
+       done
+       if [ $# -eq 0 ]; then
+           echo "-P 0"
+       fi
+       ) $rc_flags"
+}
+
+load_rc_config $name
+run_rc_command "$1"
index e897822ecc1205dcf43a3145038d986b895fc075..21aa84df5c21bd416b5c060983bb5e75478ad7d5 100644 (file)
@@ -29,23 +29,3 @@ CLEANFILES += \
        # end
 
 EXTRA_DIST += qpb/qpb.proto
-SUFFIXES += .proto .pb-c.c .pb-c.h
-
-if HAVE_PROTOBUF
-
-# Rules
-.proto.pb.h:
-       $(PROTOC) -I$(top_srcdir) --cpp_out=$(top_builddir) $^
-
-AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V))
-am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY))
-am__v_PROTOC_C_0 = @echo "  PROTOC_C" $@;
-am__v_PROTOC_C_1 =
-
-.proto.pb-c.c:
-       $(AM_V_PROTOC_C)$(PROTOC_C) -I$(top_srcdir) --c_out=$(top_builddir) $^
-       $(AM_V_GEN)$(SED) -e '1i#include "config.h"' -i $@
-.pb-c.c.pb-c.h:
-       @/bin/true
-
-endif  # HAVE_PROTOBUF
index 14973ba890e48e94487c03bb88f8a50b49173e2d..4dec84b8fb3ce09c0122d29813c26a398f996de5 100644 (file)
@@ -667,6 +667,7 @@ fi
 %{_sbindir}/ospfd
 %{_sbindir}/ripd
 %{_sbindir}/bgpd
+%{_sbindir}/mgmtd
 %exclude %{_sbindir}/ssd
 %if %{with_watchfrr}
     %{_sbindir}/watchfrr
@@ -716,6 +717,9 @@ fi
 %{_libdir}/frr/modules/dplane_fpm_nl.so
 %{_libdir}/frr/modules/zebra_irdp.so
 %{_libdir}/frr/modules/bgpd_bmp.so
+%{_libdir}/libfrr_pb.so*
+%{_libdir}/libfrrfpm_pb.so*
+%{_libdir}/libmgmt_be_nb.so*
 %{_bindir}/*
 %config(noreplace) %{configdir}/[!v]*.conf*
 %config(noreplace) %attr(750,%{frr_user},%{frr_user}) %{configdir}/daemons
@@ -775,6 +779,8 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons
 %{_libdir}/lib*.so
 %dir %{_includedir}/%{name}
 %{_includedir}/%{name}/*.h
+%dir %{_includedir}/%{name}/mgmtd
+%{_includedir}/%{name}/mgmtd/*.h
 %dir %{_includedir}/%{name}/ospfd
 %{_includedir}/%{name}/ospfd/*.h
 %if %{with_bfdd}
index 2427bfff7771fc343f242998f5006e215f5c0084..c487e7e5f28c0dd552d99902c50f5dcd92e28989 100644 (file)
@@ -40,6 +40,7 @@ pathd=no
 #
 vtysh_enable=yes
 zebra_options="  -A 127.0.0.1 -s 90000000"
+mgmtd_options="  -A 127.0.0.1"
 bgpd_options="   -A 127.0.0.1"
 ospfd_options="  -A 127.0.0.1"
 ospf6d_options=" -A ::1"
index 1ffdade54f21126891adc9721868a9f8c311b064..c9d48d0279fdab6dc35857e22061372e5bb2f58d 100755 (executable)
@@ -27,7 +27,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
 # Local Daemon selection may be done by using /etc/frr/daemons.
 # See /usr/share/doc/frr/README.Debian.gz for further information.
 # Keep zebra first and do not list watchfrr!
-DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
+DAEMONS="mgmtd zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
 MAX_INSTANCES=5
 RELOAD_SCRIPT="$D_PATH/frr-reload.py"
 
index f1db3a73d528dd90624c251f1ed28ab8e8c5cb27..e26c2947146c1d106929f6292c3f4486ebff9927 100755 (executable)
@@ -35,7 +35,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
 # - keep zebra first
 # - watchfrr does NOT belong in this list
 
-DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
+DAEMONS="zebra mgmtd bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd"
 RELOAD_SCRIPT="$D_PATH/frr-reload.py"
 
 #
@@ -99,7 +99,7 @@ daemon_list() {
        for daemon in $DAEMONS; do
                eval cfg=\$$daemon
                eval inst=\$${daemon}_instances
-               [ "$daemon" = zebra -o "$daemon" = staticd ] && cfg=yes
+               [ "$daemon" = zebra -o "$daemon" = staticd -o "$daemon" = mgmtd ] && cfg=yes
                if [ -n "$cfg" -a "$cfg" != "no" -a "$cfg" != "0" ]; then
                        if ! daemon_prep "$daemon" "$inst"; then
                                continue
index 200427fb6e1d48aa6bffb84d90a1fa5cef3fb03e..50970f26c91ba609da906028e3759ab9c0cce0b3 100644 (file)
@@ -120,6 +120,7 @@ static void vtysh_pager_envdef(bool fallback)
 /* --- */
 
 struct vtysh_client vtysh_client[] = {
+       {.name = "mgmtd", .flag = VTYSH_MGMTD},
        {.name = "zebra", .flag = VTYSH_ZEBRA},
        {.name = "ripd", .flag = VTYSH_RIPD},
        {.name = "ripngd", .flag = VTYSH_RIPNGD},
index 538837391b86bf8ab4ee7df9c79cc53ebb4f0bc0..1a3b1a0de3cac8ee32598e1790d27b955638d0c2 100644 (file)
@@ -34,6 +34,7 @@ extern struct thread_master *master;
 #define VTYSH_VRRPD     0x40000
 #define VTYSH_PATHD     0x80000
 #define VTYSH_PIM6D     0x100000
+#define VTYSH_MGMTD     0x200000
 
 #define VTYSH_WAS_ACTIVE (-2)
 
@@ -42,7 +43,12 @@ extern struct thread_master *master;
 /* watchfrr is not in ALL since library CLI functions should not be
  * run on it (logging & co. should stay in a fixed/frozen config, and
  * things like prefix lists are not even initialised) */
-#define VTYSH_ALL        VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_PIM6D|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD|VTYSH_PATHD
+#define VTYSH_ALL                                                              \
+       VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \
+               VTYSH_LDPD | VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD |           \
+               VTYSH_PIM6D | VTYSH_NHRPD | VTYSH_EIGRPD | VTYSH_BABELD |      \
+               VTYSH_SHARPD | VTYSH_PBRD | VTYSH_STATICD | VTYSH_BFDD |       \
+               VTYSH_FABRICD | VTYSH_VRRPD | VTYSH_PATHD | VTYSH_MGMTD
 #define VTYSH_ACL         VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_PIM6D|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA
 #define VTYSH_AFFMAP VTYSH_ZEBRA | VTYSH_ISISD
 #define VTYSH_RMAP       VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD
@@ -52,7 +58,7 @@ extern struct thread_master *master;
                VTYSH_EIGRPD | VTYSH_BABELD | VTYSH_PBRD | VTYSH_FABRICD |     \
                VTYSH_VRRPD
 #define VTYSH_INTERFACE VTYSH_INTERFACE_SUBSET | VTYSH_BGPD
-#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_STATICD
+#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_STATICD | VTYSH_MGMTD
 #define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D
 /* Daemons who can process nexthop-group configs */
 #define VTYSH_NH_GROUP    VTYSH_PBRD|VTYSH_SHARPD