mgmtd: Bringup MGMTD daemon and datastore module support

Features added in this commit:
1. Bringup/shutdown new management daemon 'mgmtd' along with FRR.
2. Support for Startup, Candidate and Running DBs.
3. Lock/Unlock DS feature using pthread lock.
4. Load config from a JSON file onto candidate DS.
5. Save config to a JSON file from running/candidate DS.
6. Dump candidate or running DS contents on the terminal or a file in
   JSON/XML format.
7. Maintaining commit history (Full rollback support to be added in
   future commits).
8. Addition of debug commands.

Co-authored-by: Yash Ranjan <ranjany@vmware.com>
Co-authored-by: Abhinay Ramesh <rabhinay@vmware.com>
Co-authored-by: Ujwal P <ujwalp@vmware.com>
Signed-off-by: Pushpasis Sarkar <pushpasis@gmail.com>
This commit is contained in:
Christian Hopps 2023-03-08 17:22:09 -05:00
parent ed851381b5
commit 1c84efe4fa
27 changed files with 2681 additions and 47 deletions

View 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

View 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
# 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])
# 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])

View 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"

View 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";
}

View File

@ -613,6 +613,7 @@ enum nb_client {
NB_CLIENT_SYSREPO,
NB_CLIENT_GRPC,
NB_CLIENT_PCEP,
NB_CLIENT_MGMTD_SERVER,
};
/* Northbound context. */

1
mgmtd/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
mgmtd

10
mgmtd/Makefile Normal file
View File

@ -0,0 +1,10 @@
all: ALWAYS
@$(MAKE) -s -C .. mgmtd/mgmtd
%: ALWAYS
@$(MAKE) -s -C .. mgmtd/$@
Makefile:
#nothing
ALWAYS:
.PHONY: ALWAYS makefiles
.SUFFIXES:

49
mgmtd/mgmt.c Normal file
View File

@ -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();
}

94
mgmtd/mgmt.h Normal file
View File

@ -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 */

20
mgmtd/mgmt_defines.h Normal file
View File

@ -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 */

643
mgmtd/mgmt_ds.c Normal file
View File

@ -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);
}

402
mgmtd/mgmt_ds.h Normal file
View File

@ -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_ */

271
mgmtd/mgmt_main.c Normal file
View File

@ -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;
}

21
mgmtd/mgmt_memory.c Normal file
View File

@ -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");

16
mgmtd/mgmt_memory.h Normal file
View File

@ -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 */

210
mgmtd/mgmt_test_fe Executable file
View File

@ -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

272
mgmtd/mgmt_vty.c Normal file
View File

@ -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).
*/
}

506
mgmtd/mgmt_vty.c.safe Normal file
View File

@ -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.
*/
}

42
mgmtd/subdir.am Normal file
View File

@ -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)

44
pkgsrc/mgmtd.sh.in Normal file
View File

@ -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"

View 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

View 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}

View 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"

View File

@ -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"

View File

@ -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

View 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},

View 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