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