forked from Mirror/frr
lib: introduce new northbound API
Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
parent
75082dafb5
commit
1c2facd12d
|
@ -44,6 +44,9 @@ ForEachMacros:
|
|||
- FOR_ALL_INTERFACES
|
||||
- FOR_ALL_INTERFACES_ADDRESSES
|
||||
- JSON_FOREACH
|
||||
# libyang
|
||||
- LY_TREE_FOR
|
||||
- LY_TREE_DFS_BEGIN
|
||||
# zebra
|
||||
- RE_DEST_FOREACH_ROUTE
|
||||
- RE_DEST_FOREACH_ROUTE_SAFE
|
||||
|
|
|
@ -95,9 +95,11 @@ noinst_LIBRARIES =
|
|||
nodist_noinst_DATA =
|
||||
lib_LTLIBRARIES =
|
||||
module_LTLIBRARIES =
|
||||
libyang_plugins_LTLIBRARIES =
|
||||
pkginclude_HEADERS =
|
||||
nodist_pkginclude_HEADERS =
|
||||
dist_examples_DATA =
|
||||
dist_yangmodels_DATA =
|
||||
man_MANS =
|
||||
vtysh_scan =
|
||||
|
||||
|
@ -108,6 +110,7 @@ vtysh_scan =
|
|||
$(AUTOMAKE_DUMMY)install-moduleLTLIBRARIES: install-libLTLIBRARIES
|
||||
$(AUTOMAKE_DUMMY)install-binPROGRAMS: install-libLTLIBRARIES
|
||||
$(AUTOMAKE_DUMMY)install-sbinPROGRAMS: install-libLTLIBRARIES
|
||||
$(AUTOMAKE_DUMMY)install-libyang_pluginsLTLIBRARIES: install-libLTLIBRARIES
|
||||
|
||||
include doc/subdir.am
|
||||
include doc/user/subdir.am
|
||||
|
@ -141,6 +144,8 @@ include pimd/subdir.am
|
|||
include pbrd/subdir.am
|
||||
include staticd/subdir.am
|
||||
include bfdd/subdir.am
|
||||
include yang/subdir.am
|
||||
include yang/libyang_plugins/subdir.am
|
||||
|
||||
include vtysh/subdir.am
|
||||
include tests/subdir.am
|
||||
|
|
|
@ -137,7 +137,7 @@ void sighup(void)
|
|||
zlog_info("bgpd restarting!");
|
||||
|
||||
/* Reload config file. */
|
||||
vty_read_config(bgpd_di.config_file, config_default);
|
||||
vty_read_config(NULL, bgpd_di.config_file, config_default);
|
||||
|
||||
/* Try to return to normal operation. */
|
||||
}
|
||||
|
|
39
configure.ac
39
configure.ac
|
@ -91,6 +91,12 @@ AC_ARG_WITH([moduledir], [AS_HELP_STRING([--with-moduledir=DIR], [module directo
|
|||
])
|
||||
AC_SUBST([moduledir], [$moduledir])
|
||||
|
||||
yangmodelsdir="\${datarootdir}/yang"
|
||||
AC_SUBST([yangmodelsdir], [$yangmodelsdir])
|
||||
|
||||
libyang_pluginsdir="\${libdir}/frr/libyang_plugins"
|
||||
AC_SUBST(libyang_pluginsdir)
|
||||
|
||||
AC_ARG_ENABLE(tcmalloc,
|
||||
AS_HELP_STRING([--enable-tcmalloc], [Turn on tcmalloc]),
|
||||
[case "${enableval}" in
|
||||
|
@ -417,6 +423,8 @@ AC_ARG_ENABLE(bgp-vnc,
|
|||
AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support]))
|
||||
AC_ARG_ENABLE(snmp,
|
||||
AS_HELP_STRING([--enable-snmp], [enable SNMP support for agentx]))
|
||||
AC_ARG_ENABLE(config_rollbacks,
|
||||
AS_HELP_STRING([--enable-config-rollbacks], [enable configuration rollbacks (requires sqlite3)]))
|
||||
AC_ARG_ENABLE(zeromq,
|
||||
AS_HELP_STRING([--enable-zeromq], [enable ZeroMQ handler (libfrrzmq)]))
|
||||
AC_ARG_WITH(libpam,
|
||||
|
@ -1563,6 +1571,28 @@ AM_CONDITIONAL([SNMP], [test "x${SNMP_METHOD}" != "x"])
|
|||
AC_SUBST(SNMP_LIBS)
|
||||
AC_SUBST(SNMP_CFLAGS)
|
||||
|
||||
dnl ---------------
|
||||
dnl libyang
|
||||
dnl ---------------
|
||||
PKG_CHECK_MODULES(libyang, [libyang >= 0.16.7], , [
|
||||
AC_MSG_ERROR([libyang (>= 0.16.7) was not found on your system.])
|
||||
])
|
||||
|
||||
dnl ---------------
|
||||
dnl configuration rollbacks
|
||||
dnl ---------------
|
||||
SQLITE3=false
|
||||
if test "$enable_config_rollbacks" = "yes"; then
|
||||
PKG_CHECK_MODULES(sqlite3,[sqlite3], [
|
||||
AC_DEFINE(HAVE_CONFIG_ROLLBACKS,1,Enable configuration rollbacks)
|
||||
AC_DEFINE(HAVE_SQLITE3,1,Enable sqlite3 database)
|
||||
SQLITE3=true
|
||||
], [
|
||||
AC_MSG_ERROR([--enable-config-rollbacks given but sqlite3 was not found on your system.])
|
||||
])
|
||||
fi
|
||||
AM_CONDITIONAL(SQLITE3, $SQLITE3)
|
||||
|
||||
dnl ---------------
|
||||
dnl math
|
||||
dnl ---------------
|
||||
|
@ -2004,6 +2034,7 @@ AC_DEFINE_UNQUOTED(LDPD_SOCKET, "$frr_statedir/ldpd.sock",ldpd control socket)
|
|||
AC_DEFINE_UNQUOTED(ZEBRA_SERV_PATH, "$frr_statedir/zserv.api",zebra api socket)
|
||||
AC_DEFINE_UNQUOTED(BFDD_CONTROL_SOCKET, "$frr_statedir/bfdd.sock", bfdd control socket)
|
||||
AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$frr_statedir",daemon vty directory)
|
||||
AC_DEFINE_UNQUOTED(DAEMON_DB_DIR, "$frr_statedir",daemon database directory)
|
||||
|
||||
dnl autoconf does this, but it does it too late...
|
||||
test "x$prefix" = xNONE && prefix=$ac_default_prefix
|
||||
|
@ -2021,17 +2052,25 @@ CFG_SYSCONF="$sysconfdir"
|
|||
CFG_SBIN="$sbindir"
|
||||
CFG_STATE="$frr_statedir"
|
||||
CFG_MODULE="$moduledir"
|
||||
CFG_YANGMODELS="$yangmodelsdir"
|
||||
CFG_LIBYANG_PLUGINS="$libyang_pluginsdir"
|
||||
for I in 1 2 3 4 5 6 7 8 9 10; do
|
||||
eval CFG_SYSCONF="\"$CFG_SYSCONF\""
|
||||
eval CFG_SBIN="\"$CFG_SBIN\""
|
||||
eval CFG_STATE="\"$CFG_STATE\""
|
||||
eval CFG_MODULE="\"$CFG_MODULE\""
|
||||
eval CFG_YANGMODELS="\"$CFG_YANGMODELS\""
|
||||
eval CFG_LIBYANG_PLUGINS="\"$CFG_LIBYANG_PLUGINS\""
|
||||
done
|
||||
AC_SUBST(CFG_SYSCONF)
|
||||
AC_SUBST(CFG_SBIN)
|
||||
AC_SUBST(CFG_STATE)
|
||||
AC_SUBST(CFG_MODULE)
|
||||
AC_SUBST(CFG_YANGMODELS)
|
||||
AC_SUBST(CFG_LIBYANG_PLUGINS)
|
||||
AC_DEFINE_UNQUOTED(MODULE_PATH, "$CFG_MODULE", path to modules)
|
||||
AC_DEFINE_UNQUOTED(YANG_MODELS_PATH, "$CFG_YANGMODELS", path to YANG data models)
|
||||
AC_DEFINE_UNQUOTED(LIBYANG_PLUGINS_PATH, "$CFG_LIBYANG_PLUGINS", path to libyang plugins)
|
||||
|
||||
dnl ------------------------------------
|
||||
dnl Enable RPKI and add librtr to libs
|
||||
|
|
|
@ -478,6 +478,10 @@ These options apply to all |PACKAGE_NAME| daemons.
|
|||
When initializing the daemon, allow the specification of a default
|
||||
log level at startup from one of the specified levels.
|
||||
|
||||
.. option:: --tcli
|
||||
|
||||
Enable the transactional CLI mode.
|
||||
|
||||
.. _loadable-module-support:
|
||||
|
||||
Loadable Module Support
|
||||
|
|
|
@ -222,6 +222,10 @@ options from the list below.
|
|||
the COMMIT (git hash) and TOKEN (codecov upload token) environment variables
|
||||
be set.
|
||||
|
||||
.. option:: --enable-config-rollbacks
|
||||
|
||||
Build with configuration rollback support. Requires SQLite3.
|
||||
|
||||
You may specify any combination of the above options to the configure
|
||||
script. By default, the executables are placed in :file:`/usr/local/sbin`
|
||||
and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/`
|
||||
|
|
|
@ -138,7 +138,7 @@ sighup(void)
|
|||
* and build a new configuartion from scratch.
|
||||
*/
|
||||
ldp_config_reset(vty_conf);
|
||||
vty_read_config(ldpd_di.config_file, config_default);
|
||||
vty_read_config(NULL, ldpd_di.config_file, config_default);
|
||||
ldp_config_apply(NULL, vty_conf);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "jhash.h"
|
||||
#include "hook.h"
|
||||
#include "lib_errors.h"
|
||||
#include "northbound_cli.h"
|
||||
|
||||
DEFINE_MTYPE(LIB, HOST, "Host config")
|
||||
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
|
||||
|
@ -81,6 +82,7 @@ const char *node_names[] = {
|
|||
"config", // CONFIG_NODE,
|
||||
"debug", // DEBUG_NODE,
|
||||
"vrf debug", // VRF_DEBUG_NODE,
|
||||
"northbound debug", // NORTHBOUND_DEBUG_NODE,
|
||||
"vnc debug", // DEBUG_VNC_NODE,
|
||||
"aaa", // AAA_NODE,
|
||||
"keychain", // KEYCHAIN_NODE,
|
||||
|
@ -718,11 +720,14 @@ vector cmd_describe_command(vector vline, struct vty *vty, int *status)
|
|||
|
||||
if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
|
||||
enum node_type onode;
|
||||
int orig_xpath_index;
|
||||
vector shifted_vline;
|
||||
unsigned int index;
|
||||
|
||||
onode = vty->node;
|
||||
orig_xpath_index = vty->xpath_index;
|
||||
vty->node = ENABLE_NODE;
|
||||
vty->xpath_index = 0;
|
||||
/* We can try it on enable node, cos' the vty is authenticated
|
||||
*/
|
||||
|
||||
|
@ -737,6 +742,7 @@ vector cmd_describe_command(vector vline, struct vty *vty, int *status)
|
|||
|
||||
vector_free(shifted_vline);
|
||||
vty->node = onode;
|
||||
vty->xpath_index = orig_xpath_index;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1075,14 +1081,17 @@ int cmd_execute_command(vector vline, struct vty *vty,
|
|||
{
|
||||
int ret, saved_ret = 0;
|
||||
enum node_type onode, try_node;
|
||||
int orig_xpath_index;
|
||||
|
||||
onode = try_node = vty->node;
|
||||
orig_xpath_index = vty->xpath_index;
|
||||
|
||||
if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
|
||||
vector shifted_vline;
|
||||
unsigned int index;
|
||||
|
||||
vty->node = ENABLE_NODE;
|
||||
vty->xpath_index = 0;
|
||||
/* We can try it on enable node, cos' the vty is authenticated
|
||||
*/
|
||||
|
||||
|
@ -1097,6 +1106,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
|
|||
|
||||
vector_free(shifted_vline);
|
||||
vty->node = onode;
|
||||
vty->xpath_index = orig_xpath_index;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1113,6 +1123,8 @@ int cmd_execute_command(vector vline, struct vty *vty,
|
|||
while (vty->node > CONFIG_NODE) {
|
||||
try_node = node_parent(try_node);
|
||||
vty->node = try_node;
|
||||
if (vty->xpath_index > 0)
|
||||
vty->xpath_index--;
|
||||
ret = cmd_execute_command_real(vline, FILTER_RELAXED,
|
||||
vty, cmd);
|
||||
if (ret == CMD_SUCCESS || ret == CMD_WARNING
|
||||
|
@ -1122,6 +1134,7 @@ int cmd_execute_command(vector vline, struct vty *vty,
|
|||
}
|
||||
/* no command succeeded, reset the vty to the original node */
|
||||
vty->node = onode;
|
||||
vty->xpath_index = orig_xpath_index;
|
||||
}
|
||||
|
||||
/* return command status for original node */
|
||||
|
@ -1285,7 +1298,6 @@ int command_config_read_one_line(struct vty *vty,
|
|||
uint32_t line_num, int use_daemon)
|
||||
{
|
||||
vector vline;
|
||||
int saved_node;
|
||||
int ret;
|
||||
|
||||
vline = cmd_make_strvec(vty->buf);
|
||||
|
@ -1303,14 +1315,16 @@ int command_config_read_one_line(struct vty *vty,
|
|||
&& ret != CMD_SUCCESS && ret != CMD_WARNING
|
||||
&& ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
|
||||
&& vty->node != CONFIG_NODE) {
|
||||
|
||||
saved_node = vty->node;
|
||||
int saved_node = vty->node;
|
||||
int saved_xpath_index = vty->xpath_index;
|
||||
|
||||
while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
|
||||
&& !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
|
||||
&& ret != CMD_SUCCESS && ret != CMD_WARNING
|
||||
&& vty->node > CONFIG_NODE) {
|
||||
vty->node = node_parent(vty->node);
|
||||
if (vty->xpath_index > 0)
|
||||
vty->xpath_index--;
|
||||
ret = cmd_execute_command_strict(vline, vty, cmd);
|
||||
}
|
||||
|
||||
|
@ -1320,6 +1334,7 @@ int command_config_read_one_line(struct vty *vty,
|
|||
&& !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
|
||||
&& ret != CMD_SUCCESS && ret != CMD_WARNING) {
|
||||
vty->node = saved_node;
|
||||
vty->xpath_index = saved_xpath_index;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1377,6 +1392,12 @@ DEFUN (config_terminal,
|
|||
vty_out(vty, "VTY configuration is locked by other VTY\n");
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
|
||||
vty->private_config = false;
|
||||
vty->candidate_config = vty_shared_candidate_config;
|
||||
if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
|
||||
vty->candidate_config_base = nb_config_dup(running_config);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1500,6 +1521,9 @@ void cmd_exit(struct vty *vty)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (vty->xpath_index > 0)
|
||||
vty->xpath_index--;
|
||||
}
|
||||
|
||||
/* ALIAS_FIXME */
|
||||
|
@ -1576,6 +1600,9 @@ DEFUN (config_end,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
vty->xpath_index = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -2785,6 +2812,8 @@ void install_default(enum node_type node)
|
|||
install_element(node, &show_running_config_cmd);
|
||||
|
||||
install_element(node, &autocomplete_cmd);
|
||||
|
||||
nb_cli_install_default(node);
|
||||
}
|
||||
|
||||
/* Initialize command interface. Install basic nodes and commands.
|
||||
|
|
|
@ -77,6 +77,7 @@ enum node_type {
|
|||
CONFIG_NODE, /* Config node. Default mode of config file. */
|
||||
DEBUG_NODE, /* Debug node. */
|
||||
VRF_DEBUG_NODE, /* Vrf Debug node. */
|
||||
NORTHBOUND_DEBUG_NODE, /* Northbound Debug node. */
|
||||
DEBUG_VNC_NODE, /* Debug VNC node. */
|
||||
AAA_NODE, /* AAA node. */
|
||||
KEYCHAIN_NODE, /* Key-chain node. */
|
||||
|
|
325
lib/db.c
Normal file
325
lib/db.c
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Rafael Zalamena <rzalamena@gmail.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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Rafael Zalamena <rzalamena@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include "db.h"
|
||||
#include "log.h"
|
||||
|
||||
static struct sqlite3 *dbp;
|
||||
|
||||
/*
|
||||
* Initialize the database in path.
|
||||
*
|
||||
* It's possible to use in memory database with ':memory:' path.
|
||||
*/
|
||||
int db_init(const char *path_fmt, ...)
|
||||
{
|
||||
char path[BUFSIZ];
|
||||
va_list ap;
|
||||
|
||||
if (dbp)
|
||||
return -1;
|
||||
|
||||
va_start(ap, path_fmt);
|
||||
vsnprintf(path, sizeof(path), path_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (sqlite3_open_v2(path, &dbp,
|
||||
(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE), NULL)
|
||||
!= SQLITE_OK) {
|
||||
if (dbp == NULL) {
|
||||
zlog_warn("%s: failed to open dabatase '%s'", __func__,
|
||||
path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
zlog_warn("%s: failed to open database '%s': %s", __func__,
|
||||
path, sqlite3_errmsg(dbp));
|
||||
if (sqlite3_close_v2(dbp) != SQLITE_OK)
|
||||
zlog_warn("%s: failed to terminate database", __func__);
|
||||
dbp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Closes the database if open. */
|
||||
int db_close(void)
|
||||
{
|
||||
if (dbp == NULL)
|
||||
return 0;
|
||||
|
||||
if (sqlite3_close_v2(dbp) != SQLITE_OK) {
|
||||
zlog_warn("%s: failed to terminate database", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to handle formating. */
|
||||
static int db_vbindf(struct sqlite3_stmt *ss, const char *fmt, va_list vl)
|
||||
{
|
||||
const char *sptr = fmt;
|
||||
int column = 1;
|
||||
const char *str;
|
||||
void *blob;
|
||||
uint64_t uinteger64;
|
||||
uint32_t uinteger;
|
||||
int vlen;
|
||||
|
||||
while (*sptr) {
|
||||
if (*sptr != '%') {
|
||||
sptr++;
|
||||
continue;
|
||||
}
|
||||
if (sptr++ && *sptr == 0)
|
||||
break;
|
||||
|
||||
switch (*sptr) {
|
||||
case 'i':
|
||||
uinteger = va_arg(vl, uint32_t);
|
||||
if (sqlite3_bind_int(ss, column++, uinteger)
|
||||
!= SQLITE_OK)
|
||||
return -1;
|
||||
break;
|
||||
case 'd':
|
||||
uinteger64 = va_arg(vl, uint64_t);
|
||||
if (sqlite3_bind_int64(ss, column++, uinteger64)
|
||||
!= SQLITE_OK)
|
||||
return -1;
|
||||
break;
|
||||
case 's':
|
||||
str = va_arg(vl, const char *);
|
||||
vlen = va_arg(vl, int);
|
||||
if (sqlite3_bind_text(ss, column++, str, vlen,
|
||||
SQLITE_STATIC)
|
||||
!= SQLITE_OK)
|
||||
return -1;
|
||||
break;
|
||||
case 'b':
|
||||
blob = va_arg(vl, void *);
|
||||
vlen = va_arg(vl, int);
|
||||
if (sqlite3_bind_blob(ss, column++, blob, vlen,
|
||||
SQLITE_STATIC)
|
||||
!= SQLITE_OK)
|
||||
return -1;
|
||||
break;
|
||||
case 'n':
|
||||
if (sqlite3_bind_null(ss, column++) != SQLITE_OK)
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
zlog_warn("%s: invalid format '%c'", __func__, *sptr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Binds values using format to the database query.
|
||||
*
|
||||
* Might be used to bind variables to a query, insert or update.
|
||||
*/
|
||||
int db_bindf(struct sqlite3_stmt *ss, const char *fmt, ...)
|
||||
{
|
||||
va_list vl;
|
||||
int result;
|
||||
|
||||
va_start(vl, fmt);
|
||||
result = db_vbindf(ss, fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Prepares an statement to the database with the statement length. */
|
||||
struct sqlite3_stmt *db_prepare_len(const char *stmt, int stmtlen)
|
||||
{
|
||||
struct sqlite3_stmt *ss;
|
||||
int c;
|
||||
|
||||
if (dbp == NULL)
|
||||
return NULL;
|
||||
|
||||
c = sqlite3_prepare_v2(dbp, stmt, stmtlen, &ss, NULL);
|
||||
if (ss == NULL) {
|
||||
zlog_warn("%s: failed to prepare (%d:%s)", __func__, c,
|
||||
sqlite3_errmsg(dbp));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ss;
|
||||
}
|
||||
|
||||
/* Prepares an statement to the database. */
|
||||
struct sqlite3_stmt *db_prepare(const char *stmt)
|
||||
{
|
||||
return db_prepare_len(stmt, strlen(stmt));
|
||||
}
|
||||
|
||||
/* Run a prepared statement. */
|
||||
int db_run(struct sqlite3_stmt *ss)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = sqlite3_step(ss);
|
||||
switch (result) {
|
||||
case SQLITE_BUSY:
|
||||
/* TODO handle busy database. */
|
||||
break;
|
||||
|
||||
case SQLITE_OK:
|
||||
/*
|
||||
* SQLITE_DONE just causes confusion since it means the query went OK,
|
||||
* but it has a different value.
|
||||
*/
|
||||
case SQLITE_DONE:
|
||||
result = SQLITE_OK;
|
||||
break;
|
||||
|
||||
case SQLITE_ROW:
|
||||
/* NOTHING */
|
||||
/* It is expected to receive SQLITE_ROW on search queries. */
|
||||
break;
|
||||
|
||||
default:
|
||||
zlog_warn("%s: step failed (%d:%s)", __func__, result,
|
||||
sqlite3_errstr(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Helper function to load format to variables. */
|
||||
static int db_vloadf(struct sqlite3_stmt *ss, const char *fmt, va_list vl)
|
||||
{
|
||||
const char *sptr = fmt;
|
||||
int column = 0;
|
||||
const char **str;
|
||||
void *blob;
|
||||
const void *blobsrc;
|
||||
uint64_t *uinteger64;
|
||||
uint32_t *uinteger;
|
||||
int vlen;
|
||||
int dlen;
|
||||
int columncount;
|
||||
|
||||
columncount = sqlite3_column_count(ss);
|
||||
if (columncount == 0)
|
||||
return -1;
|
||||
|
||||
while (*sptr) {
|
||||
if (*sptr != '%') {
|
||||
sptr++;
|
||||
continue;
|
||||
}
|
||||
if (sptr++ && *sptr == 0)
|
||||
break;
|
||||
|
||||
switch (*sptr) {
|
||||
case 'i':
|
||||
uinteger = va_arg(vl, uint32_t *);
|
||||
*uinteger = sqlite3_column_int(ss, column);
|
||||
break;
|
||||
case 'd':
|
||||
uinteger64 = va_arg(vl, uint64_t *);
|
||||
*uinteger64 = sqlite3_column_int64(ss, column);
|
||||
break;
|
||||
case 's':
|
||||
str = va_arg(vl, const char **);
|
||||
*str = (const char *)sqlite3_column_text(ss, column);
|
||||
break;
|
||||
case 'b':
|
||||
blob = va_arg(vl, void *);
|
||||
vlen = va_arg(vl, int);
|
||||
dlen = sqlite3_column_bytes(ss, column);
|
||||
blobsrc = sqlite3_column_blob(ss, column);
|
||||
memcpy(blob, blobsrc, MIN(vlen, dlen));
|
||||
break;
|
||||
default:
|
||||
zlog_warn("%s: invalid format '%c'", __func__, *sptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
column++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Function to load format from database row. */
|
||||
int db_loadf(struct sqlite3_stmt *ss, const char *fmt, ...)
|
||||
{
|
||||
va_list vl;
|
||||
int result;
|
||||
|
||||
va_start(vl, fmt);
|
||||
result = db_vloadf(ss, fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Finalize query and return memory. */
|
||||
void db_finalize(struct sqlite3_stmt **ss)
|
||||
{
|
||||
sqlite3_finalize(*ss);
|
||||
*ss = NULL;
|
||||
}
|
||||
|
||||
/* Execute one or more statements. */
|
||||
int db_execute(const char *stmt_fmt, ...)
|
||||
{
|
||||
char stmt[BUFSIZ];
|
||||
va_list ap;
|
||||
|
||||
if (dbp == NULL)
|
||||
return -1;
|
||||
|
||||
va_start(ap, stmt_fmt);
|
||||
vsnprintf(stmt, sizeof(stmt), stmt_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (sqlite3_exec(dbp, stmt, NULL, 0, NULL) != SQLITE_OK) {
|
||||
zlog_warn("%s: failed to execute statement(s): %s", __func__,
|
||||
sqlite3_errmsg(dbp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
52
lib/db.h
Normal file
52
lib/db.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Rafael Zalamena <rzalamena@gmail.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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Rafael Zalamena <rzalamena@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _FRR_DB_H_
|
||||
#define _FRR_DB_H_
|
||||
#ifdef HAVE_SQLITE3
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
extern int db_init(const char *path_fmt, ...);
|
||||
extern int db_close(void);
|
||||
extern int db_bindf(struct sqlite3_stmt *ss, const char *fmt, ...);
|
||||
extern struct sqlite3_stmt *db_prepare_len(const char *stmt, int stmtlen);
|
||||
extern struct sqlite3_stmt *db_prepare(const char *stmt);
|
||||
extern int db_run(struct sqlite3_stmt *ss);
|
||||
extern int db_loadf(struct sqlite3_stmt *ss, const char *fmt, ...);
|
||||
extern void db_finalize(struct sqlite3_stmt **ss);
|
||||
extern int db_execute(const char *stmt_fmt, ...);
|
||||
|
||||
#endif /* HAVE_SQLITE3 */
|
||||
#endif /* _FRR_DB_H_ */
|
168
lib/lib_errors.c
168
lib/lib_errors.c
|
@ -68,6 +68,84 @@ static struct log_ref ferr_lib_warn[] = {
|
|||
.description = "The VRF subsystem, during initialization, has found a parsing error with input it has received",
|
||||
.suggestion = "Check the length of the vrf name and adjust accordingly",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_DATA_TRUNCATED,
|
||||
.title = "YANG data truncation",
|
||||
.description = "The northbound subsystem has detected that YANG data has been truncated as the given buffer wasn't big enough",
|
||||
.suggestion = "Gather log data and open an Issue",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
||||
.title = "Unknown YANG data path",
|
||||
.description = "The northbound subsystem has detected an unknown YANG data path",
|
||||
.suggestion = "Gather log data and open an Issue",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
.title = "Unable to load YANG module translator",
|
||||
.description = "The northbound subsystem has detected an error while loading a YANG module translator",
|
||||
.suggestion = "Ensure the YANG module translator file is valid. See documentation for further information.",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_TRANSLATION_ERROR,
|
||||
.title = "YANG translation error",
|
||||
.description = "The northbound subsystem has detected an error while performing a YANG XPath translation",
|
||||
.suggestion = "Gather log data and open an Issue",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_DATABASE,
|
||||
.title = "The northbound database wasn't initialized correctly",
|
||||
.description = "An error occurred while initializing the northbound database. As a result, the configuration rollback feature won't work as expected.",
|
||||
.suggestion = "Ensure permissions are correct for FRR files, users and groups are correct."
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CB_UNNEEDED,
|
||||
.title = "Unneeded northbound callback",
|
||||
.description = "The northbound subsystem, during initialization, has detected a callback that doesn't need to be implemented",
|
||||
.suggestion = "Check if the installed FRR YANG modules are in sync with the FRR binaries",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CB_CONFIG,
|
||||
.title = "A northbound configuration callback has failed",
|
||||
.description = "The northbound subsystem has detected that a callback used to process a configuration change has returned an error",
|
||||
.suggestion = "The log message should contain further details on the specific error that occurred; investigate the reported error.",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CB_STATE,
|
||||
.title = "A northbound callback for operational data has failed",
|
||||
.description = "The northbound subsystem has detected that a callback used to fetch operational data has returned an error",
|
||||
.suggestion = "Gather log data and open an Issue",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CB_RPC,
|
||||
.title = "A northbound RPC callback has failed",
|
||||
.description = "The northbound subsystem has detected that a callback used to process YANG RPCs/actions has returned an error",
|
||||
.suggestion = "The log message should contain further details on the specific error that occurred; investigate the reported error.",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CANDIDATE_INVALID,
|
||||
.title = "Invalid candidate configuration",
|
||||
.description = "The northbound subsystem failed to validate a candidate configuration",
|
||||
.suggestion = "Check the log messages to see the validation errors and edit the candidate configuration to fix them",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CANDIDATE_EDIT_ERROR,
|
||||
.title = "Failure to edit a candidate configuration",
|
||||
.description = "The northbound subsystem failed to edit a candidate configuration",
|
||||
.suggestion = "This is a bug; please report it"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_TRANSACTION_CREATION_FAILED,
|
||||
.title = "Failure to create a configuration transaction",
|
||||
.description = "The northbound subsystem failed to create a configuration transaction",
|
||||
.suggestion = "The log message should contain further details on the specific error that occurred; investigate the reported error.",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_TRANSACTION_RECORD_FAILED,
|
||||
.title = "Failure to record a configuration transaction",
|
||||
.description = "The northbound subsystem failed to record a configuration transaction in the northbound database",
|
||||
.suggestion = "Gather log data and open an Issue",
|
||||
},
|
||||
{
|
||||
.code = END_FERR,
|
||||
},
|
||||
|
@ -152,6 +230,96 @@ static struct log_ref ferr_lib_err[] = {
|
|||
.description = "FRR was not compiled with support for a particular feature, or it is not available on the current platform",
|
||||
.suggestion = "Recompile FRR with the feature enabled, or find out what platforms support the feature"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_MODULE_LOAD,
|
||||
.title = "Unable to load YANG module from the file system",
|
||||
.description = "The northbound subsystem has detected an error while loading a YANG module from the file system",
|
||||
.suggestion = "Ensure all FRR YANG modules were installed correctly in the system.",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_MODULE_LOADED_ALREADY,
|
||||
.title = "Attempt to load a YANG module that is already loaded",
|
||||
.description = "The northbound subsystem has detected an attempt to load a YANG module that is already loaded",
|
||||
.suggestion = "This is a bug; please report it"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_DATA_CONVERT,
|
||||
.title = "YANG data conversion error",
|
||||
.description = "An error has occurred while converting a YANG data value from string to binary representation or vice-versa",
|
||||
.suggestion = "Open an Issue with all relevant log files and restart FRR"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_YANG_DNODE_NOT_FOUND,
|
||||
.title = "YANG data node not found",
|
||||
.description = "The northbound subsystem failed to find a YANG data node that was supposed to exist",
|
||||
.suggestion = "This is a bug; please report it"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CB_MISSING,
|
||||
.title = "Missing northbound callback",
|
||||
.description = "The northbound subsystem, during initialization, has detected a missing callback for one node of the loaded YANG modules",
|
||||
.suggestion = "Check if the installed FRR YANG modules are in sync with the FRR binaries",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CB_INVALID_PRIO,
|
||||
.title = "Norhtbound callback has an invalid priority",
|
||||
.description = "The northbound subsystem, during initialization, has detected a callback whose priority is invalid",
|
||||
.suggestion = "Check if the installed FRR YANG modules are in sync with the FRR binaries",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_NB_CBS_VALIDATION,
|
||||
.title = "Failure to validate the northbound callbacks",
|
||||
.description = "The northbound subsystem, during initialization, has detected one or more errors while loading the northbound callbacks",
|
||||
.suggestion = "Check if the installed FRR YANG modules are in sync with the FRR binaries",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_LIBYANG,
|
||||
.title = "The libyang library returned an error",
|
||||
.description = "The northbound subsystem has detected that the libyang library returned an error",
|
||||
.suggestion = "Open an Issue with all relevant log files and restart FRR"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_LIBYANG_PLUGIN_LOAD,
|
||||
.title = "Failure to load a libyang plugin",
|
||||
.description = "The northbound subsystem, during initialization, has detected that a libyang plugin failed to be loaded",
|
||||
.suggestion = "Check if the FRR libyang plugins were installed correctly in the system",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_CONFD_INIT,
|
||||
.title = "ConfD initialization error",
|
||||
.description = "Upon startup FRR failed to properly initialize and startup the ConfD northbound plugin",
|
||||
.suggestion = "Check if ConfD is installed correctly in the system. Also, check if the confd daemon is running.",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_CONFD_DATA_CONVERT,
|
||||
.title = "ConfD data conversion error",
|
||||
.description = "An error has occurred while converting a ConfD data value (binary) to a string",
|
||||
.suggestion = "Open an Issue with all relevant log files and restart FRR"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_LIBCONFD,
|
||||
.title = "libconfd error",
|
||||
.description = "The northbound subsystem has detected that the libconfd library returned an error",
|
||||
.suggestion = "Open an Issue with all relevant log files and restart FRR"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_SYSREPO_INIT,
|
||||
.title = "Sysrepo initialization error",
|
||||
.description = "Upon startup FRR failed to properly initialize and startup the Sysrepo northbound plugin",
|
||||
.suggestion = "Check if Sysrepo is installed correctly in the system",
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_SYSREPO_DATA_CONVERT,
|
||||
.title = "Sysrepo data conversion error",
|
||||
.description = "An error has occurred while converting a YANG data value to the Sysrepo format",
|
||||
.suggestion = "Open an Issue with all relevant log files and restart FRR"
|
||||
},
|
||||
{
|
||||
.code = EC_LIB_LIBSYSREPO,
|
||||
.title = "libsysrepo error",
|
||||
.description = "The northbound subsystem has detected that the libsysrepo library returned an error",
|
||||
.suggestion = "Open an Issue with all relevant log files and restart FRR"
|
||||
},
|
||||
{
|
||||
.code = END_FERR,
|
||||
}
|
||||
|
|
|
@ -44,6 +44,34 @@ enum lib_log_refs {
|
|||
EC_LIB_RMAP_RECURSION_LIMIT,
|
||||
EC_LIB_BACKUP_CONFIG,
|
||||
EC_LIB_VRF_LENGTH,
|
||||
EC_LIB_YANG_MODULE_LOAD,
|
||||
EC_LIB_YANG_MODULE_LOADED_ALREADY,
|
||||
EC_LIB_YANG_DATA_CONVERT,
|
||||
EC_LIB_YANG_DATA_TRUNCATED,
|
||||
EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
||||
EC_LIB_YANG_DNODE_NOT_FOUND,
|
||||
EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
EC_LIB_YANG_TRANSLATION_ERROR,
|
||||
EC_LIB_NB_DATABASE,
|
||||
EC_LIB_NB_CB_UNNEEDED,
|
||||
EC_LIB_NB_CB_MISSING,
|
||||
EC_LIB_NB_CB_INVALID_PRIO,
|
||||
EC_LIB_NB_CBS_VALIDATION,
|
||||
EC_LIB_NB_CB_CONFIG,
|
||||
EC_LIB_NB_CB_STATE,
|
||||
EC_LIB_NB_CB_RPC,
|
||||
EC_LIB_NB_CANDIDATE_INVALID,
|
||||
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
|
||||
EC_LIB_NB_TRANSACTION_CREATION_FAILED,
|
||||
EC_LIB_NB_TRANSACTION_RECORD_FAILED,
|
||||
EC_LIB_LIBYANG,
|
||||
EC_LIB_LIBYANG_PLUGIN_LOAD,
|
||||
EC_LIB_CONFD_INIT,
|
||||
EC_LIB_CONFD_DATA_CONVERT,
|
||||
EC_LIB_LIBCONFD,
|
||||
EC_LIB_SYSREPO_INIT,
|
||||
EC_LIB_SYSREPO_DATA_CONVERT,
|
||||
EC_LIB_LIBSYSREPO,
|
||||
};
|
||||
|
||||
extern void lib_error_init(void);
|
||||
|
|
75
lib/libfrr.c
75
lib/libfrr.c
|
@ -36,6 +36,8 @@
|
|||
#include "module.h"
|
||||
#include "network.h"
|
||||
#include "lib_errors.h"
|
||||
#include "db.h"
|
||||
#include "northbound_cli.h"
|
||||
|
||||
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
|
||||
DEFINE_KOOH(frr_early_fini, (), ())
|
||||
|
@ -43,6 +45,9 @@ DEFINE_KOOH(frr_fini, (), ())
|
|||
|
||||
const char frr_sysconfdir[] = SYSCONFDIR;
|
||||
const char frr_vtydir[] = DAEMON_VTY_DIR;
|
||||
#ifdef HAVE_SQLITE3
|
||||
const char frr_dbdir[] = DAEMON_DB_DIR;
|
||||
#endif
|
||||
const char frr_moduledir[] = MODULE_PATH;
|
||||
|
||||
char frr_protoname[256] = "NONE";
|
||||
|
@ -51,6 +56,9 @@ char frr_protonameinst[256] = "NONE";
|
|||
char config_default[512];
|
||||
char frr_zclientpath[256];
|
||||
static char pidfile_default[512];
|
||||
#ifdef HAVE_SQLITE3
|
||||
static char dbfile_default[512];
|
||||
#endif
|
||||
static char vtypath_default[256];
|
||||
|
||||
bool debug_memstats_at_exit = 0;
|
||||
|
@ -82,6 +90,8 @@ static void opt_extend(const struct optspec *os)
|
|||
#define OPTION_MODULEDIR 1002
|
||||
#define OPTION_LOG 1003
|
||||
#define OPTION_LOGLEVEL 1004
|
||||
#define OPTION_TCLI 1005
|
||||
#define OPTION_DB_FILE 1006
|
||||
|
||||
static const struct option lo_always[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
|
@ -92,6 +102,7 @@ static const struct option lo_always[] = {
|
|||
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
|
||||
{"log", required_argument, NULL, OPTION_LOG},
|
||||
{"log-level", required_argument, NULL, OPTION_LOGLEVEL},
|
||||
{"tcli", no_argument, NULL, OPTION_TCLI},
|
||||
{NULL}};
|
||||
static const struct optspec os_always = {
|
||||
"hvdM:",
|
||||
|
@ -102,13 +113,17 @@ static const struct optspec os_always = {
|
|||
" --vty_socket Override vty socket path\n"
|
||||
" --moduledir Override modules directory\n"
|
||||
" --log Set Logging to stdout, syslog, or file:<name>\n"
|
||||
" --log-level Set Logging Level to use, debug, info, warn, etc\n",
|
||||
" --log-level Set Logging Level to use, debug, info, warn, etc\n"
|
||||
" --tcli Use transaction-based CLI\n",
|
||||
lo_always};
|
||||
|
||||
|
||||
static const struct option lo_cfg_pid_dry[] = {
|
||||
{"pid_file", required_argument, NULL, 'i'},
|
||||
{"config_file", required_argument, NULL, 'f'},
|
||||
#ifdef HAVE_SQLITE3
|
||||
{"db_file", required_argument, NULL, OPTION_DB_FILE},
|
||||
#endif
|
||||
{"pathspace", required_argument, NULL, 'N'},
|
||||
{"dryrun", no_argument, NULL, 'C'},
|
||||
{"terminal", no_argument, NULL, 't'},
|
||||
|
@ -117,6 +132,9 @@ static const struct optspec os_cfg_pid_dry = {
|
|||
"f:i:CtN:",
|
||||
" -f, --config_file Set configuration file name\n"
|
||||
" -i, --pid_file Set process identifier file name\n"
|
||||
#ifdef HAVE_SQLITE3
|
||||
" --db_file Set database file name\n"
|
||||
#endif
|
||||
" -N, --pathspace Insert prefix into config & socket paths\n"
|
||||
" -C, --dryrun Check configuration for validity and exit\n"
|
||||
" -t, --terminal Open terminal session on stdio\n"
|
||||
|
@ -289,11 +307,17 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
|
|||
frr_sysconfdir, di->name);
|
||||
snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid",
|
||||
frr_vtydir, di->name);
|
||||
#ifdef HAVE_SQLITE3
|
||||
snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db",
|
||||
frr_dbdir, di->name);
|
||||
#endif
|
||||
|
||||
strlcpy(frr_protoname, di->logname, sizeof(frr_protoname));
|
||||
strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));
|
||||
|
||||
strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath));
|
||||
|
||||
di->cli_mode = FRR_CLI_CLASSIC;
|
||||
}
|
||||
|
||||
void frr_opt_add(const char *optstr, const struct option *longopts,
|
||||
|
@ -380,6 +404,13 @@ static int frr_opt(int opt)
|
|||
}
|
||||
di->pathspace = optarg;
|
||||
break;
|
||||
#ifdef HAVE_SQLITE3
|
||||
case OPTION_DB_FILE:
|
||||
if (di->flags & FRR_NO_CFG_PID_DRY)
|
||||
return 1;
|
||||
di->db_file = optarg;
|
||||
break;
|
||||
#endif
|
||||
case 'C':
|
||||
if (di->flags & FRR_NO_CFG_PID_DRY)
|
||||
return 1;
|
||||
|
@ -444,6 +475,9 @@ static int frr_opt(int opt)
|
|||
}
|
||||
di->module_path = optarg;
|
||||
break;
|
||||
case OPTION_TCLI:
|
||||
di->cli_mode = FRR_CLI_TRANSACTIONAL;
|
||||
break;
|
||||
case 'u':
|
||||
if (di->flags & FRR_NO_PRIVSEP)
|
||||
return 1;
|
||||
|
@ -556,6 +590,10 @@ struct thread_master *frr_init(void)
|
|||
frr_sysconfdir, p_pathspace, di->name, p_instance);
|
||||
snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid",
|
||||
frr_vtydir, p_pathspace, di->name, p_instance);
|
||||
#ifdef HAVE_SQLITE3
|
||||
snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db",
|
||||
frr_dbdir, p_pathspace, di->name, p_instance);
|
||||
#endif
|
||||
|
||||
zprivs_preinit(di->privs);
|
||||
|
||||
|
@ -597,19 +635,39 @@ struct thread_master *frr_init(void)
|
|||
master = thread_master_create(NULL);
|
||||
signal_init(master, di->n_signals, di->signals);
|
||||
|
||||
#ifdef HAVE_SQLITE3
|
||||
if (!di->db_file)
|
||||
di->db_file = dbfile_default;
|
||||
db_init(di->db_file);
|
||||
#endif
|
||||
|
||||
if (di->flags & FRR_LIMITED_CLI)
|
||||
cmd_init(-1);
|
||||
else
|
||||
cmd_init(1);
|
||||
|
||||
vty_init(master);
|
||||
memory_init();
|
||||
|
||||
log_ref_init();
|
||||
lib_error_init();
|
||||
|
||||
yang_init();
|
||||
nb_init(di->yang_modules, di->n_yang_modules);
|
||||
|
||||
return master;
|
||||
}
|
||||
|
||||
const char *frr_get_progname(void)
|
||||
{
|
||||
return di ? di->progname : NULL;
|
||||
}
|
||||
|
||||
enum frr_cli_mode frr_get_cli_mode(void)
|
||||
{
|
||||
return di ? di->cli_mode : FRR_CLI_CLASSIC;
|
||||
}
|
||||
|
||||
static int rcvd_signal = 0;
|
||||
|
||||
static void rcv_signal(int signum)
|
||||
|
@ -752,17 +810,23 @@ static void frr_daemonize(void)
|
|||
*/
|
||||
static int frr_config_read_in(struct thread *t)
|
||||
{
|
||||
if (!vty_read_config(di->config_file, config_default) &&
|
||||
if (!vty_read_config(NULL, di->config_file, config_default) &&
|
||||
di->backup_config_file) {
|
||||
char *orig = XSTRDUP(MTYPE_TMP, host_config_get());
|
||||
|
||||
zlog_info("Attempting to read backup config file: %s specified",
|
||||
di->backup_config_file);
|
||||
vty_read_config(di->backup_config_file, config_default);
|
||||
vty_read_config(NULL, di->backup_config_file, config_default);
|
||||
|
||||
host_config_set(orig);
|
||||
XFREE(MTYPE_TMP, orig);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the shared candidate after reading the startup configuration.
|
||||
*/
|
||||
nb_config_replace(vty_shared_candidate_config, running_config, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -963,6 +1027,11 @@ void frr_fini(void)
|
|||
/* memory_init -> nothing needed */
|
||||
vty_terminate();
|
||||
cmd_terminate();
|
||||
nb_terminate();
|
||||
yang_terminate();
|
||||
#ifdef HAVE_SQLITE3
|
||||
db_close();
|
||||
#endif
|
||||
log_ref_fini();
|
||||
zprivs_terminate(di->privs);
|
||||
/* signal_init -> nothing needed */
|
||||
|
|
15
lib/libfrr.h
15
lib/libfrr.h
|
@ -28,6 +28,7 @@
|
|||
#include "getopt.h"
|
||||
#include "module.h"
|
||||
#include "hook.h"
|
||||
#include "northbound.h"
|
||||
|
||||
/* The following options disable specific command line options that
|
||||
* are not applicable for a particular daemon.
|
||||
|
@ -45,6 +46,11 @@
|
|||
*/
|
||||
#define FRR_DETACH_LATER (1 << 5)
|
||||
|
||||
enum frr_cli_mode {
|
||||
FRR_CLI_CLASSIC = 0,
|
||||
FRR_CLI_TRANSACTIONAL,
|
||||
};
|
||||
|
||||
struct frr_daemon_info {
|
||||
unsigned flags;
|
||||
|
||||
|
@ -60,11 +66,15 @@ struct frr_daemon_info {
|
|||
bool dryrun;
|
||||
bool daemon_mode;
|
||||
bool terminal;
|
||||
enum frr_cli_mode cli_mode;
|
||||
|
||||
struct thread *read_in;
|
||||
const char *config_file;
|
||||
const char *backup_config_file;
|
||||
const char *pid_file;
|
||||
#ifdef HAVE_SQLITE3
|
||||
const char *db_file;
|
||||
#endif
|
||||
const char *vty_path;
|
||||
const char *module_path;
|
||||
const char *pathspace;
|
||||
|
@ -80,6 +90,9 @@ struct frr_daemon_info {
|
|||
size_t n_signals;
|
||||
|
||||
struct zebra_privs_t *privs;
|
||||
|
||||
const struct frr_yang_module_info **yang_modules;
|
||||
size_t n_yang_modules;
|
||||
};
|
||||
|
||||
/* execname is the daemon's executable (and pidfile and configfile) name,
|
||||
|
@ -108,6 +121,8 @@ extern int frr_getopt(int argc, char *const argv[], int *longindex);
|
|||
extern void frr_help_exit(int status);
|
||||
|
||||
extern struct thread_master *frr_init(void);
|
||||
extern const char *frr_get_progname(void);
|
||||
extern enum frr_cli_mode frr_get_cli_mode(void);
|
||||
|
||||
DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
|
||||
extern void frr_config_fork(void);
|
||||
|
|
1200
lib/northbound.c
Normal file
1200
lib/northbound.c
Normal file
File diff suppressed because it is too large
Load diff
780
lib/northbound.h
Normal file
780
lib/northbound.h
Normal file
|
@ -0,0 +1,780 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _FRR_NORTHBOUND_H_
|
||||
#define _FRR_NORTHBOUND_H_
|
||||
|
||||
#include "hook.h"
|
||||
#include "yang.h"
|
||||
#include "linklist.h"
|
||||
#include "openbsd-tree.h"
|
||||
|
||||
/* Forward declaration(s). */
|
||||
struct vty;
|
||||
|
||||
/* Northbound events. */
|
||||
enum nb_event {
|
||||
/*
|
||||
* The configuration callback is supposed to verify that the changes are
|
||||
* valid and can be applied.
|
||||
*/
|
||||
NB_EV_VALIDATE,
|
||||
|
||||
/*
|
||||
* The configuration callback is supposed to prepare all resources
|
||||
* required to apply the changes.
|
||||
*/
|
||||
NB_EV_PREPARE,
|
||||
|
||||
/*
|
||||
* Transaction has failed, the configuration callback needs to release
|
||||
* all resources previously allocated.
|
||||
*/
|
||||
NB_EV_ABORT,
|
||||
|
||||
/*
|
||||
* The configuration changes need to be applied. The changes can't be
|
||||
* rejected at this point (errors are logged and ignored).
|
||||
*/
|
||||
NB_EV_APPLY,
|
||||
};
|
||||
|
||||
/*
|
||||
* Northbound operations.
|
||||
*
|
||||
* Refer to the documentation comments of nb_callbacks for more details.
|
||||
*/
|
||||
enum nb_operation {
|
||||
NB_OP_CREATE,
|
||||
NB_OP_MODIFY,
|
||||
NB_OP_DELETE,
|
||||
NB_OP_MOVE,
|
||||
NB_OP_APPLY_FINISH,
|
||||
NB_OP_GET_ELEM,
|
||||
NB_OP_GET_NEXT,
|
||||
NB_OP_GET_KEYS,
|
||||
NB_OP_LOOKUP_ENTRY,
|
||||
NB_OP_RPC,
|
||||
};
|
||||
|
||||
union nb_resource {
|
||||
int fd;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
struct nb_callbacks {
|
||||
/*
|
||||
* Configuration callback.
|
||||
*
|
||||
* A presence container, list entry, leaf-list entry or leaf of type
|
||||
* empty has been created.
|
||||
*
|
||||
* For presence-containers and list entries, the callback is supposed to
|
||||
* initialize the default values of its children (if any) from the YANG
|
||||
* models.
|
||||
*
|
||||
* event
|
||||
* The transaction phase. Refer to the documentation comments of
|
||||
* nb_event for more details.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node that is being created.
|
||||
*
|
||||
* resource
|
||||
* Pointer to store resource(s) allocated during the NB_EV_PREPARE
|
||||
* phase. The same pointer can be used during the NB_EV_ABORT and
|
||||
* NB_EV_APPLY phases to either release or make use of the allocated
|
||||
* resource(s). It's set to NULL when the event is NB_EV_VALIDATE.
|
||||
*
|
||||
* Returns:
|
||||
* - NB_OK on success.
|
||||
* - NB_ERR_VALIDATION when a validation error occurred.
|
||||
* - NB_ERR_RESOURCE when the callback failed to allocate a resource.
|
||||
* - NB_ERR_INCONSISTENCY when an inconsistency was detected.
|
||||
* - NB_ERR for other errors.
|
||||
*/
|
||||
int (*create)(enum nb_event event, const struct lyd_node *dnode,
|
||||
union nb_resource *resource);
|
||||
|
||||
/*
|
||||
* Configuration callback.
|
||||
*
|
||||
* The value of a leaf has been modified.
|
||||
*
|
||||
* List keys don't need to implement this callback. When a list key is
|
||||
* modified, the northbound treats this as if the list was deleted and a
|
||||
* new one created with the updated key value.
|
||||
*
|
||||
* event
|
||||
* The transaction phase. Refer to the documentation comments of
|
||||
* nb_event for more details.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node that is being modified
|
||||
*
|
||||
* resource
|
||||
* Pointer to store resource(s) allocated during the NB_EV_PREPARE
|
||||
* phase. The same pointer can be used during the NB_EV_ABORT and
|
||||
* NB_EV_APPLY phases to either release or make use of the allocated
|
||||
* resource(s). It's set to NULL when the event is NB_EV_VALIDATE.
|
||||
*
|
||||
* Returns:
|
||||
* - NB_OK on success.
|
||||
* - NB_ERR_VALIDATION when a validation error occurred.
|
||||
* - NB_ERR_RESOURCE when the callback failed to allocate a resource.
|
||||
* - NB_ERR_INCONSISTENCY when an inconsistency was detected.
|
||||
* - NB_ERR for other errors.
|
||||
*/
|
||||
int (*modify)(enum nb_event event, const struct lyd_node *dnode,
|
||||
union nb_resource *resource);
|
||||
|
||||
/*
|
||||
* Configuration callback.
|
||||
*
|
||||
* A presence container, list entry, leaf-list entry or optional leaf
|
||||
* has been deleted.
|
||||
*
|
||||
* The callback is supposed to delete the entire configuration object,
|
||||
* including its children when they exist.
|
||||
*
|
||||
* event
|
||||
* The transaction phase. Refer to the documentation comments of
|
||||
* nb_event for more details.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node that is being deleted.
|
||||
*
|
||||
* Returns:
|
||||
* - NB_OK on success.
|
||||
* - NB_ERR_VALIDATION when a validation error occurred.
|
||||
* - NB_ERR_INCONSISTENCY when an inconsistency was detected.
|
||||
* - NB_ERR for other errors.
|
||||
*/
|
||||
int (*delete)(enum nb_event event, const struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Configuration callback.
|
||||
*
|
||||
* A list entry or leaf-list entry has been moved. Only applicable when
|
||||
* the "ordered-by user" statement is present.
|
||||
*
|
||||
* event
|
||||
* The transaction phase. Refer to the documentation comments of
|
||||
* nb_event for more details.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node that is being moved.
|
||||
*
|
||||
* Returns:
|
||||
* - NB_OK on success.
|
||||
* - NB_ERR_VALIDATION when a validation error occurred.
|
||||
* - NB_ERR_INCONSISTENCY when an inconsistency was detected.
|
||||
* - NB_ERR for other errors.
|
||||
*/
|
||||
int (*move)(enum nb_event event, const struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Optional configuration callback.
|
||||
*
|
||||
* The 'apply_finish' callbacks are called after all other callbacks
|
||||
* during the apply phase (NB_EV_APPLY). These callbacks are called only
|
||||
* under one of the following two cases:
|
||||
* - The data node has been created or modified (but not deleted);
|
||||
* - Any change was made within the descendants of the data node (e.g. a
|
||||
* child leaf was modified, created or deleted).
|
||||
*
|
||||
* In the second case above, the 'apply_finish' callback is called only
|
||||
* once even if multiple changes occurred within the descendants of the
|
||||
* data node.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node associated with the 'apply_finish' callback.
|
||||
*/
|
||||
void (*apply_finish)(const struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Operational data callback.
|
||||
*
|
||||
* The callback function should return the value of a specific leaf or
|
||||
* inform if a typeless value (presence containers or leafs of type
|
||||
* empty) exists or not.
|
||||
*
|
||||
* xpath
|
||||
* YANG data path of the data we want to get.
|
||||
*
|
||||
* list_entry
|
||||
* Pointer to list entry.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created yang_data structure, or NULL to indicate
|
||||
* the absence of data.
|
||||
*/
|
||||
struct yang_data *(*get_elem)(const char *xpath,
|
||||
const void *list_entry);
|
||||
|
||||
/*
|
||||
* Operational data callback for YANG lists.
|
||||
*
|
||||
* The callback function should return the next entry in the list. The
|
||||
* 'list_entry' parameter will be NULL on the first invocation.
|
||||
*
|
||||
* xpath
|
||||
* Data path of the YANG list.
|
||||
*
|
||||
* list_entry
|
||||
* Pointer to list entry.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to the next entry in the list, or NULL to signal that the
|
||||
* end of the list was reached.
|
||||
*/
|
||||
const void *(*get_next)(const char *xpath, const void *list_entry);
|
||||
|
||||
/*
|
||||
* Operational data callback for YANG lists.
|
||||
*
|
||||
* The callback function should fill the 'keys' parameter based on the
|
||||
* given list_entry.
|
||||
*
|
||||
* list_entry
|
||||
* Pointer to list entry.
|
||||
*
|
||||
* keys
|
||||
* Structure to be filled based on the attributes of the provided
|
||||
* list entry.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
int (*get_keys)(const void *list_entry, struct yang_list_keys *keys);
|
||||
|
||||
/*
|
||||
* Operational data callback for YANG lists.
|
||||
*
|
||||
* The callback function should return a list entry based on the list
|
||||
* keys given as a parameter.
|
||||
*
|
||||
* keys
|
||||
* Structure containing the keys of the list entry.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to the list entry if found, or NULL if not found.
|
||||
*/
|
||||
const void *(*lookup_entry)(const struct yang_list_keys *keys);
|
||||
|
||||
/*
|
||||
* RPC and action callback.
|
||||
*
|
||||
* Both 'input' and 'output' are lists of 'yang_data' structures. The
|
||||
* callback should fetch all the input parameters from the 'input' list,
|
||||
* and add output parameters to the 'output' list if necessary.
|
||||
*
|
||||
* xpath
|
||||
* XPath of the YANG RPC or action.
|
||||
*
|
||||
* input
|
||||
* Read-only list of input parameters.
|
||||
*
|
||||
* output
|
||||
* List of output parameters to be populated by the callback.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
int (*rpc)(const char *xpath, const struct list *input,
|
||||
struct list *output);
|
||||
|
||||
/*
|
||||
* Optional callback to show the CLI command associated to the given
|
||||
* YANG data node.
|
||||
*
|
||||
* vty
|
||||
* The vty terminal to dump the configuration to.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node that should be shown in the form of a CLI
|
||||
* command.
|
||||
*
|
||||
* show_defaults
|
||||
* Specify whether to display default configuration values or not.
|
||||
* This parameter can be ignored most of the time since the
|
||||
* northbound doesn't call this callback for default leaves or
|
||||
* non-presence containers that contain only default child nodes.
|
||||
* The exception are commands associated to multiple configuration
|
||||
* nodes, in which case it might be desirable to hide one or more
|
||||
* parts of the command when this parameter is set to false.
|
||||
*/
|
||||
void (*cli_show)(struct vty *vty, struct lyd_node *dnode,
|
||||
bool show_defaults);
|
||||
};
|
||||
|
||||
/*
|
||||
* Northbound-specific data that is allocated for each schema node of the native
|
||||
* YANG modules.
|
||||
*/
|
||||
struct nb_node {
|
||||
/* Back pointer to the libyang schema node. */
|
||||
const struct lys_node *snode;
|
||||
|
||||
/* Data path of this YANG node. */
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
/* Priority - lower priorities are processed first. */
|
||||
uint32_t priority;
|
||||
|
||||
/* Callbacks implemented for this node. */
|
||||
struct nb_callbacks cbs;
|
||||
|
||||
/*
|
||||
* Pointer to the parent node (disconsidering non-presence containers).
|
||||
*/
|
||||
struct nb_node *parent;
|
||||
|
||||
/* Pointer to the nearest parent list, if any. */
|
||||
struct nb_node *parent_list;
|
||||
|
||||
#ifdef HAVE_CONFD
|
||||
/* ConfD hash value corresponding to this YANG path. */
|
||||
int confd_hash;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct frr_yang_module_info {
|
||||
/* YANG module name. */
|
||||
const char *name;
|
||||
|
||||
/* Northbound callbacks. */
|
||||
const struct {
|
||||
/* Data path of this YANG node. */
|
||||
const char *xpath;
|
||||
|
||||
/* Callbacks implemented for this node. */
|
||||
struct nb_callbacks cbs;
|
||||
|
||||
/* Priority - lower priorities are processed first. */
|
||||
uint32_t priority;
|
||||
} nodes[];
|
||||
};
|
||||
|
||||
/* Northbound error codes. */
|
||||
enum nb_error {
|
||||
NB_OK = 0,
|
||||
NB_ERR,
|
||||
NB_ERR_NO_CHANGES,
|
||||
NB_ERR_NOT_FOUND,
|
||||
NB_ERR_LOCKED,
|
||||
NB_ERR_VALIDATION,
|
||||
NB_ERR_RESOURCE,
|
||||
NB_ERR_INCONSISTENCY,
|
||||
};
|
||||
|
||||
/* Default priority. */
|
||||
#define NB_DFLT_PRIORITY (UINT32_MAX / 2)
|
||||
|
||||
/* Default maximum of configuration rollbacks to store. */
|
||||
#define NB_DLFT_MAX_CONFIG_ROLLBACKS 20
|
||||
|
||||
/* Northbound clients. */
|
||||
enum nb_client {
|
||||
NB_CLIENT_CLI = 0,
|
||||
};
|
||||
|
||||
/* Northbound configuration. */
|
||||
struct nb_config {
|
||||
struct lyd_node *dnode;
|
||||
uint32_t version;
|
||||
};
|
||||
|
||||
/* Northbound configuration callback. */
|
||||
struct nb_config_cb {
|
||||
RB_ENTRY(nb_config_cb) entry;
|
||||
enum nb_operation operation;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const struct nb_node *nb_node;
|
||||
const struct lyd_node *dnode;
|
||||
};
|
||||
RB_HEAD(nb_config_cbs, nb_config_cb);
|
||||
RB_PROTOTYPE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
|
||||
|
||||
/* Northbound configuration change. */
|
||||
struct nb_config_change {
|
||||
struct nb_config_cb cb;
|
||||
union nb_resource resource;
|
||||
bool prepare_ok;
|
||||
};
|
||||
|
||||
/* Northbound configuration transaction. */
|
||||
struct nb_transaction {
|
||||
enum nb_client client;
|
||||
char comment[80];
|
||||
struct nb_config *config;
|
||||
struct nb_config_cbs changes;
|
||||
};
|
||||
|
||||
DECLARE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
|
||||
(xpath, arguments))
|
||||
|
||||
extern int debug_northbound;
|
||||
extern struct nb_config *running_config;
|
||||
|
||||
/*
|
||||
* Find the northbound node corresponding to a YANG data path.
|
||||
*
|
||||
* xpath
|
||||
* XPath to search for (with or without predicates).
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to northbound node if found, NULL otherwise.
|
||||
*/
|
||||
extern struct nb_node *nb_node_find(const char *xpath);
|
||||
|
||||
/*
|
||||
* Create a new northbound configuration.
|
||||
*
|
||||
* dnode
|
||||
* Pointer to a libyang data node containing the configuration data. If NULL
|
||||
* is given, an empty configuration will be created.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created northbound configuration.
|
||||
*/
|
||||
extern struct nb_config *nb_config_new(struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Delete a northbound configuration.
|
||||
*
|
||||
* config
|
||||
* Pointer to the config that is going to be deleted.
|
||||
*/
|
||||
extern void nb_config_free(struct nb_config *config);
|
||||
|
||||
/*
|
||||
* Duplicate a northbound configuration.
|
||||
*
|
||||
* config
|
||||
* Northbound configuration to duplicate.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to duplicated configuration.
|
||||
*/
|
||||
extern struct nb_config *nb_config_dup(const struct nb_config *config);
|
||||
|
||||
/*
|
||||
* Merge one configuration into another.
|
||||
*
|
||||
* config_dst
|
||||
* Configuration to merge to.
|
||||
*
|
||||
* config_src
|
||||
* Configuration to merge config_dst with.
|
||||
*
|
||||
* preserve_source
|
||||
* Specify whether config_src should be deleted or not after the merge
|
||||
* operation.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
extern int nb_config_merge(struct nb_config *config_dst,
|
||||
struct nb_config *config_src, bool preserve_source);
|
||||
|
||||
/*
|
||||
* Replace one configuration by another.
|
||||
*
|
||||
* config_dst
|
||||
* Configuration to be replaced.
|
||||
*
|
||||
* config_src
|
||||
* Configuration to replace config_dst.
|
||||
*
|
||||
* preserve_source
|
||||
* Specify whether config_src should be deleted or not after the replace
|
||||
* operation.
|
||||
*/
|
||||
extern void nb_config_replace(struct nb_config *config_dst,
|
||||
struct nb_config *config_src,
|
||||
bool preserve_source);
|
||||
|
||||
/*
|
||||
* Edit a candidate configuration.
|
||||
*
|
||||
* candidate
|
||||
* Candidate configuration to edit.
|
||||
*
|
||||
* nb_node
|
||||
* Northbound node associated to the configuration being edited.
|
||||
*
|
||||
* operation
|
||||
* Operation to apply.
|
||||
*
|
||||
* xpath
|
||||
* XPath of the configuration node being edited.
|
||||
*
|
||||
* previous
|
||||
* Previous value of the configuration node. Should be used only when the
|
||||
* operation is NB_OP_MOVE, otherwise this parameter is ignored.
|
||||
*
|
||||
* data
|
||||
* New value of the configuration node.
|
||||
*
|
||||
* Returns:
|
||||
* - NB_OK on success.
|
||||
* - NB_ERR_NOT_FOUND when the element to be deleted was not found.
|
||||
* - NB_ERR for other errors.
|
||||
*/
|
||||
extern int nb_candidate_edit(struct nb_config *candidate,
|
||||
const struct nb_node *nb_node,
|
||||
enum nb_operation operation, const char *xpath,
|
||||
const struct yang_data *previous,
|
||||
const struct yang_data *data);
|
||||
|
||||
/*
|
||||
* Check if a candidate configuration is outdated and needs to be updated.
|
||||
*
|
||||
* candidate
|
||||
* Candidate configuration to check.
|
||||
*
|
||||
* Returns:
|
||||
* true if the candidate is outdated, false otherwise.
|
||||
*/
|
||||
extern bool nb_candidate_needs_update(const struct nb_config *candidate);
|
||||
|
||||
/*
|
||||
* Update a candidate configuration by rebasing the changes on top of the latest
|
||||
* running configuration. Resolve conflicts automatically by giving preference
|
||||
* to the changes done in the candidate configuration.
|
||||
*
|
||||
* candidate
|
||||
* Candidate configuration to update.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
extern int nb_candidate_update(struct nb_config *candidate);
|
||||
|
||||
/*
|
||||
* Validate a candidate configuration. Perform both YANG syntactic/semantic
|
||||
* validation and code-level validation using the northbound callbacks.
|
||||
*
|
||||
* WARNING: the candidate can be modified as part of the validation process
|
||||
* (e.g. add default nodes).
|
||||
*
|
||||
* candidate
|
||||
* Candidate configuration to validate.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR_VALIDATION otherwise.
|
||||
*/
|
||||
extern int nb_candidate_validate(struct nb_config *candidate);
|
||||
|
||||
/*
|
||||
* Create a new configuration transaction but do not commit it yet. Only
|
||||
* validate the candidate and prepare all resources required to apply the
|
||||
* configuration changes.
|
||||
*
|
||||
* candidate
|
||||
* Candidate configuration to commit.
|
||||
*
|
||||
* client
|
||||
* Northbound client performing the commit.
|
||||
*
|
||||
* comment
|
||||
* Optional comment describing the commit.
|
||||
*
|
||||
* transaction
|
||||
* Output parameter providing the created transaction when one is created
|
||||
* successfully. In this case, it must be either aborted using
|
||||
* nb_candidate_commit_abort() or committed using
|
||||
* nb_candidate_commit_apply().
|
||||
*
|
||||
* Returns:
|
||||
* - NB_OK on success.
|
||||
* - NB_ERR_NO_CHANGES when the candidate is identical to the running
|
||||
* configuration.
|
||||
* - NB_ERR_LOCKED when there's already another transaction in progress.
|
||||
* - NB_ERR_VALIDATION when the candidate fails the validation checks.
|
||||
* - NB_ERR_RESOURCE when the system fails to allocate resources to apply
|
||||
* the candidate configuration.
|
||||
* - NB_ERR for other errors.
|
||||
*/
|
||||
extern int nb_candidate_commit_prepare(struct nb_config *candidate,
|
||||
enum nb_client client,
|
||||
const char *comment,
|
||||
struct nb_transaction **transaction);
|
||||
|
||||
/*
|
||||
* Abort a previously created configuration transaction, releasing all resources
|
||||
* allocated during the preparation phase.
|
||||
*
|
||||
* transaction
|
||||
* Candidate configuration to abort. It's consumed by this function.
|
||||
*/
|
||||
extern void nb_candidate_commit_abort(struct nb_transaction *transaction);
|
||||
|
||||
/*
|
||||
* Commit a previously created configuration transaction.
|
||||
*
|
||||
* transaction
|
||||
* Configuration transaction to commit. It's consumed by this function.
|
||||
*
|
||||
* save_transaction
|
||||
* Specify whether the transaction should be recorded in the transactions log
|
||||
* or not.
|
||||
*
|
||||
* transaction_id
|
||||
* Optional output parameter providing the ID of the committed transaction.
|
||||
*/
|
||||
extern void nb_candidate_commit_apply(struct nb_transaction *transaction,
|
||||
bool save_transaction,
|
||||
uint32_t *transaction_id);
|
||||
|
||||
/*
|
||||
* Create a new transaction to commit a candidate configuration. This is a
|
||||
* convenience function that performs the two-phase commit protocol
|
||||
* transparently to the user. The cost is reduced flexibility, since
|
||||
* network-wide and multi-daemon transactions require the network manager to
|
||||
* take into account the results of the preparation phase of multiple managed
|
||||
* entities.
|
||||
*
|
||||
* candidate
|
||||
* Candidate configuration to commit. It's preserved regardless if the commit
|
||||
* operation fails or not.
|
||||
*
|
||||
* client
|
||||
* Northbound client performing the commit.
|
||||
*
|
||||
* save_transaction
|
||||
* Specify whether the transaction should be recorded in the transactions log
|
||||
* or not.
|
||||
*
|
||||
* comment
|
||||
* Optional comment describing the commit.
|
||||
*
|
||||
* transaction_id
|
||||
* Optional output parameter providing the ID of the committed transaction.
|
||||
*
|
||||
* Returns:
|
||||
* - NB_OK on success.
|
||||
* - NB_ERR_NO_CHANGES when the candidate is identical to the running
|
||||
* configuration.
|
||||
* - NB_ERR_LOCKED when there's already another transaction in progress.
|
||||
* - NB_ERR_VALIDATION when the candidate fails the validation checks.
|
||||
* - NB_ERR_RESOURCE when the system fails to allocate resources to apply
|
||||
* the candidate configuration.
|
||||
* - NB_ERR for other errors.
|
||||
*/
|
||||
extern int nb_candidate_commit(struct nb_config *candidate,
|
||||
enum nb_client client, bool save_transaction,
|
||||
const char *comment, uint32_t *transaction_id);
|
||||
|
||||
/*
|
||||
* Validate if the northbound operation is valid for the given node.
|
||||
*
|
||||
* operation
|
||||
* Operation we want to check.
|
||||
*
|
||||
* snode
|
||||
* libyang schema node we want to check.
|
||||
*
|
||||
* Returns:
|
||||
* true if the operation is valid, false otherwise.
|
||||
*/
|
||||
extern bool nb_operation_is_valid(enum nb_operation operation,
|
||||
const struct lys_node *snode);
|
||||
|
||||
/*
|
||||
* Send a YANG notification. This is a no-op unless the 'nb_notification_send'
|
||||
* hook was registered by a northbound plugin.
|
||||
*
|
||||
* xpath
|
||||
* XPath of the YANG notification.
|
||||
*
|
||||
* arguments
|
||||
* Linked list containing the arguments that should be sent. This list is
|
||||
* deleted after being used.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
extern int nb_notification_send(const char *xpath, struct list *arguments);
|
||||
|
||||
/*
|
||||
* Return a human-readable string representing a northbound event.
|
||||
*
|
||||
* event
|
||||
* Northbound event.
|
||||
*
|
||||
* Returns:
|
||||
* String representation of the given northbound event.
|
||||
*/
|
||||
extern const char *nb_event_name(enum nb_event event);
|
||||
|
||||
/*
|
||||
* Return a human-readable string representing a northbound operation.
|
||||
*
|
||||
* operation
|
||||
* Northbound operation.
|
||||
*
|
||||
* Returns:
|
||||
* String representation of the given northbound operation.
|
||||
*/
|
||||
extern const char *nb_operation_name(enum nb_operation operation);
|
||||
|
||||
/*
|
||||
* Return a human-readable string representing a northbound error.
|
||||
*
|
||||
* error
|
||||
* Northbound error.
|
||||
*
|
||||
* Returns:
|
||||
* String representation of the given northbound error.
|
||||
*/
|
||||
extern const char *nb_err_name(enum nb_error error);
|
||||
|
||||
/*
|
||||
* Return a human-readable string representing a northbound client.
|
||||
*
|
||||
* client
|
||||
* Northbound client.
|
||||
*
|
||||
* Returns:
|
||||
* String representation of the given northbound client.
|
||||
*/
|
||||
extern const char *nb_client_name(enum nb_client client);
|
||||
|
||||
/*
|
||||
* Initialize the northbound layer. Should be called only once during the
|
||||
* daemon initialization process.
|
||||
*
|
||||
* modules
|
||||
* Array of YANG modules to parse and initialize.
|
||||
*
|
||||
* nmodules
|
||||
* Size of the modules array.
|
||||
*/
|
||||
extern void nb_init(const struct frr_yang_module_info *modules[],
|
||||
size_t nmodules);
|
||||
|
||||
/*
|
||||
* Finish the northbound layer gracefully. Should be called only when the daemon
|
||||
* is exiting.
|
||||
*/
|
||||
extern void nb_terminate(void);
|
||||
|
||||
#endif /* _FRR_NORTHBOUND_H_ */
|
1448
lib/northbound_cli.c
Normal file
1448
lib/northbound_cli.c
Normal file
File diff suppressed because it is too large
Load diff
66
lib/northbound_cli.h
Normal file
66
lib/northbound_cli.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _FRR_NORTHBOUND_CLI_H_
|
||||
#define _FRR_NORTHBOUND_CLI_H_
|
||||
|
||||
#include "northbound.h"
|
||||
|
||||
struct cli_config_change {
|
||||
/*
|
||||
* XPath (absolute or relative) of the configuration option being
|
||||
* edited.
|
||||
*/
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
/*
|
||||
* Operation to apply (either NB_OP_CREATE, NB_OP_MODIFY or
|
||||
* NB_OP_DELETE).
|
||||
*/
|
||||
enum nb_operation operation;
|
||||
|
||||
/*
|
||||
* New value of the configuration option. Should be NULL for typeless
|
||||
* YANG data (e.g. presence-containers). For convenience, NULL can also
|
||||
* be used to restore a leaf to its default value.
|
||||
*/
|
||||
const char *value;
|
||||
};
|
||||
|
||||
/* Possible formats in which a configuration can be displayed. */
|
||||
enum nb_cfg_format {
|
||||
NB_CFG_FMT_CMDS = 0,
|
||||
NB_CFG_FMT_JSON,
|
||||
NB_CFG_FMT_XML,
|
||||
};
|
||||
|
||||
extern struct nb_config *vty_shared_candidate_config;
|
||||
|
||||
/* Prototypes. */
|
||||
extern int nb_cli_cfg_change(struct vty *vty, char *xpath_list,
|
||||
struct cli_config_change changes[], size_t size);
|
||||
extern int nb_cli_rpc(const char *xpath, struct list *input,
|
||||
struct list *output);
|
||||
extern void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *dnode,
|
||||
bool show_defaults);
|
||||
extern void nb_cli_install_default(int node);
|
||||
extern void nb_cli_init(void);
|
||||
extern void nb_cli_terminate(void);
|
||||
|
||||
#endif /* _FRR_NORTHBOUND_CLI_H_ */
|
288
lib/northbound_db.c
Normal file
288
lib/northbound_db.c
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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 "libfrr.h"
|
||||
#include "log.h"
|
||||
#include "lib_errors.h"
|
||||
#include "command.h"
|
||||
#include "db.h"
|
||||
#include "northbound.h"
|
||||
#include "northbound_db.h"
|
||||
|
||||
int nb_db_init(void)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_ROLLBACKS
|
||||
/*
|
||||
* NOTE: the delete_tail SQL trigger is used to implement a ring buffer
|
||||
* where only the last N transactions are recorded in the configuration
|
||||
* log.
|
||||
*/
|
||||
if (db_execute(
|
||||
"BEGIN TRANSACTION;\n"
|
||||
" CREATE TABLE IF NOT EXISTS transactions(\n"
|
||||
" client CHAR(32) NOT NULL,\n"
|
||||
" date DATETIME DEFAULT CURRENT_TIMESTAMP,\n"
|
||||
" comment CHAR(80) ,\n"
|
||||
" configuration TEXT NOT NULL\n"
|
||||
" );\n"
|
||||
" CREATE TRIGGER IF NOT EXISTS delete_tail\n"
|
||||
" AFTER INSERT ON transactions\n"
|
||||
" FOR EACH ROW\n"
|
||||
" BEGIN\n"
|
||||
" DELETE\n"
|
||||
" FROM\n"
|
||||
" transactions\n"
|
||||
" WHERE\n"
|
||||
" rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
|
||||
" END;\n"
|
||||
"COMMIT;",
|
||||
NB_DLFT_MAX_CONFIG_ROLLBACKS, NB_DLFT_MAX_CONFIG_ROLLBACKS)
|
||||
!= 0)
|
||||
return NB_ERR;
|
||||
#endif /* HAVE_CONFIG_ROLLBACKS */
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
|
||||
int nb_db_transaction_save(const struct nb_transaction *transaction,
|
||||
uint32_t *transaction_id)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_ROLLBACKS
|
||||
struct sqlite3_stmt *ss;
|
||||
const char *client_name;
|
||||
char *config_str = NULL;
|
||||
int ret = NB_ERR;
|
||||
|
||||
/*
|
||||
* Use a transaction to ensure consistency between the INSERT and SELECT
|
||||
* queries.
|
||||
*/
|
||||
if (db_execute("BEGIN TRANSACTION;") != 0)
|
||||
return NB_ERR;
|
||||
|
||||
ss = db_prepare(
|
||||
"INSERT INTO transactions\n"
|
||||
" (client, comment, configuration)\n"
|
||||
"VALUES\n"
|
||||
" (?, ?, ?);");
|
||||
if (!ss)
|
||||
goto exit;
|
||||
|
||||
client_name = nb_client_name(transaction->client);
|
||||
/* Always record configurations in the XML format. */
|
||||
if (lyd_print_mem(&config_str, transaction->config->dnode, LYD_XML,
|
||||
LYP_FORMAT | LYP_WITHSIBLINGS)
|
||||
!= 0)
|
||||
goto exit;
|
||||
|
||||
if (db_bindf(ss, "%s%s%s", client_name, strlen(client_name),
|
||||
transaction->comment, strlen(transaction->comment),
|
||||
config_str ? config_str : "",
|
||||
config_str ? strlen(config_str) : 0)
|
||||
!= 0)
|
||||
goto exit;
|
||||
|
||||
if (db_run(ss) != SQLITE_OK)
|
||||
goto exit;
|
||||
|
||||
db_finalize(&ss);
|
||||
|
||||
/*
|
||||
* transaction_id is an optional output parameter that provides the ID
|
||||
* of the recorded transaction.
|
||||
*/
|
||||
if (transaction_id) {
|
||||
ss = db_prepare("SELECT last_insert_rowid();");
|
||||
if (!ss)
|
||||
goto exit;
|
||||
|
||||
if (db_run(ss) != SQLITE_ROW)
|
||||
goto exit;
|
||||
|
||||
if (db_loadf(ss, "%i", transaction_id) != 0)
|
||||
goto exit;
|
||||
|
||||
db_finalize(&ss);
|
||||
}
|
||||
|
||||
if (db_execute("COMMIT;") != 0)
|
||||
goto exit;
|
||||
|
||||
ret = NB_OK;
|
||||
|
||||
exit:
|
||||
if (config_str)
|
||||
free(config_str);
|
||||
if (ss)
|
||||
db_finalize(&ss);
|
||||
if (ret != NB_OK)
|
||||
(void)db_execute("ROLLBACK TRANSACTION;");
|
||||
|
||||
return ret;
|
||||
#else /* HAVE_CONFIG_ROLLBACKS */
|
||||
return NB_OK;
|
||||
#endif /* HAVE_CONFIG_ROLLBACKS */
|
||||
}
|
||||
|
||||
struct nb_config *nb_db_transaction_load(uint32_t transaction_id)
|
||||
{
|
||||
struct nb_config *config = NULL;
|
||||
#ifdef HAVE_CONFIG_ROLLBACKS
|
||||
struct lyd_node *dnode;
|
||||
const char *config_str;
|
||||
struct sqlite3_stmt *ss;
|
||||
|
||||
ss = db_prepare(
|
||||
"SELECT\n"
|
||||
" configuration\n"
|
||||
"FROM\n"
|
||||
" transactions\n"
|
||||
"WHERE\n"
|
||||
" rowid=?;");
|
||||
if (!ss)
|
||||
return NULL;
|
||||
|
||||
if (db_bindf(ss, "%d", transaction_id) != 0)
|
||||
goto exit;
|
||||
|
||||
if (db_run(ss) != SQLITE_ROW)
|
||||
goto exit;
|
||||
|
||||
if (db_loadf(ss, "%s", &config_str) != 0)
|
||||
goto exit;
|
||||
|
||||
dnode = lyd_parse_mem(ly_native_ctx, config_str, LYD_XML,
|
||||
LYD_OPT_CONFIG);
|
||||
if (!dnode)
|
||||
flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_mem() failed",
|
||||
__func__);
|
||||
else
|
||||
config = nb_config_new(dnode);
|
||||
|
||||
exit:
|
||||
db_finalize(&ss);
|
||||
#endif /* HAVE_CONFIG_ROLLBACKS */
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
int nb_db_clear_transactions(unsigned int n_oldest)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_ROLLBACKS
|
||||
/* Delete oldest N entries. */
|
||||
if (db_execute("DELETE\n"
|
||||
"FROM\n"
|
||||
" transactions\n"
|
||||
"WHERE\n"
|
||||
" ROWID IN (\n"
|
||||
" SELECT\n"
|
||||
" ROWID\n"
|
||||
" FROM\n"
|
||||
" transactions\n"
|
||||
" ORDER BY ROWID ASC LIMIT %u\n"
|
||||
" );",
|
||||
n_oldest)
|
||||
!= 0)
|
||||
return NB_ERR;
|
||||
#endif /* HAVE_CONFIG_ROLLBACKS */
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
|
||||
int nb_db_set_max_transactions(unsigned int max)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_ROLLBACKS
|
||||
/*
|
||||
* Delete old entries if necessary and update the SQL trigger that
|
||||
* auto-deletes old entries.
|
||||
*/
|
||||
if (db_execute("BEGIN TRANSACTION;\n"
|
||||
" DELETE\n"
|
||||
" FROM\n"
|
||||
" transactions\n"
|
||||
" WHERE\n"
|
||||
" ROWID IN (\n"
|
||||
" SELECT\n"
|
||||
" ROWID\n"
|
||||
" FROM\n"
|
||||
" transactions\n"
|
||||
" ORDER BY ROWID DESC LIMIT -1 OFFSET %u\n"
|
||||
" );\n"
|
||||
" DROP TRIGGER delete_tail;\n"
|
||||
" CREATE TRIGGER delete_tail\n"
|
||||
" AFTER INSERT ON transactions\n"
|
||||
" FOR EACH ROW\n"
|
||||
" BEGIN\n"
|
||||
" DELETE\n"
|
||||
" FROM\n"
|
||||
" transactions\n"
|
||||
" WHERE\n"
|
||||
" rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
|
||||
" END;\n"
|
||||
"COMMIT;",
|
||||
max, max, max)
|
||||
!= 0)
|
||||
return NB_ERR;
|
||||
#endif /* HAVE_CONFIG_ROLLBACKS */
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
|
||||
int nb_db_transactions_iterate(void (*func)(void *arg, int transaction_id,
|
||||
const char *client_name,
|
||||
const char *date,
|
||||
const char *comment),
|
||||
void *arg)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_ROLLBACKS
|
||||
struct sqlite3_stmt *ss;
|
||||
|
||||
/* Send SQL query and parse the result. */
|
||||
ss = db_prepare(
|
||||
"SELECT\n"
|
||||
" rowid, client, date, comment\n"
|
||||
"FROM\n"
|
||||
" transactions\n"
|
||||
"ORDER BY\n"
|
||||
" rowid DESC;");
|
||||
if (!ss)
|
||||
return NB_ERR;
|
||||
|
||||
while (db_run(ss) == SQLITE_ROW) {
|
||||
int transaction_id;
|
||||
const char *client_name;
|
||||
const char *date;
|
||||
const char *comment;
|
||||
int ret;
|
||||
|
||||
ret = db_loadf(ss, "%i%s%s%s", &transaction_id, &client_name,
|
||||
&date, &comment);
|
||||
if (ret != 0)
|
||||
continue;
|
||||
|
||||
(*func)(arg, transaction_id, client_name, date, comment);
|
||||
}
|
||||
|
||||
db_finalize(&ss);
|
||||
#endif /* HAVE_CONFIG_ROLLBACKS */
|
||||
|
||||
return NB_OK;
|
||||
}
|
104
lib/northbound_db.h
Normal file
104
lib/northbound_db.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _FRR_NORTHBOUND_DB_H_
|
||||
#define _FRR_NORTHBOUND_DB_H_
|
||||
|
||||
#include "northbound.h"
|
||||
|
||||
/*
|
||||
* Initialize the northbound database.
|
||||
*
|
||||
* Currently the database is used only for storing and retrieving configuration
|
||||
* transactions.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
int nb_db_init(void);
|
||||
|
||||
/*
|
||||
* Save a configuration transaction in the northbound database.
|
||||
*
|
||||
* transaction
|
||||
* Configuration transaction to be saved.
|
||||
*
|
||||
* transaction_id
|
||||
* Output parameter providing the ID of the saved transaction.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
int nb_db_transaction_save(const struct nb_transaction *transaction,
|
||||
uint32_t *transaction_id);
|
||||
|
||||
/*
|
||||
* Load a configuration transaction from the transactions log.
|
||||
*
|
||||
* transaction_id
|
||||
* ID of the transaction to be loaded.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created configuration or NULL in the case of an error.
|
||||
*/
|
||||
extern struct nb_config *nb_db_transaction_load(uint32_t transaction_id);
|
||||
|
||||
/*
|
||||
* Delete the specified number of transactions from the transactions log.
|
||||
*
|
||||
* n_oldest
|
||||
* Number of transactions to delete.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
extern int nb_db_clear_transactions(unsigned int n_oldest);
|
||||
|
||||
/*
|
||||
* Specify the maximum number of transactions we want to record in the
|
||||
* transactions log. Note that older transactions can be removed during this
|
||||
* operation.
|
||||
*
|
||||
* max
|
||||
* New upper limit of maximum transactions to log.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
extern int nb_db_set_max_transactions(unsigned int max);
|
||||
|
||||
/*
|
||||
* Iterate over all configuration transactions stored in the northbound
|
||||
* database, sorted in descending order.
|
||||
*
|
||||
* func
|
||||
* Function to call with each configuration transaction.
|
||||
*
|
||||
* arg
|
||||
* Arbitrary argument passed as the first parameter in each call to 'func'.
|
||||
*
|
||||
* Returns:
|
||||
* NB_OK on success, NB_ERR otherwise.
|
||||
*/
|
||||
extern int nb_db_transactions_iterate(
|
||||
void (*func)(void *arg, int transaction_id, const char *client_name,
|
||||
const char *date, const char *comment),
|
||||
void *arg);
|
||||
|
||||
#endif /* _FRR_NORTHBOUND_DB_H_ */
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
lib_LTLIBRARIES += lib/libfrr.la
|
||||
lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version
|
||||
lib_libfrr_la_LIBADD = @LIBCAP@ $(UNWIND_LIBS)
|
||||
lib_libfrr_la_LIBADD = @LIBCAP@ $(UNWIND_LIBS) -lyang
|
||||
|
||||
lib_libfrr_la_SOURCES = \
|
||||
lib/agg_table.c \
|
||||
|
@ -50,6 +50,9 @@ lib_libfrr_la_SOURCES = \
|
|||
lib/netns_linux.c \
|
||||
lib/netns_other.c \
|
||||
lib/nexthop_group.c \
|
||||
lib/northbound.c \
|
||||
lib/northbound_cli.c \
|
||||
lib/northbound_db.c \
|
||||
lib/openbsd-tree.c \
|
||||
lib/pid_output.c \
|
||||
lib/plist.c \
|
||||
|
@ -80,6 +83,9 @@ lib_libfrr_la_SOURCES = \
|
|||
lib/vty.c \
|
||||
lib/wheel.c \
|
||||
lib/workqueue.c \
|
||||
lib/yang.c \
|
||||
lib/yang_translator.c \
|
||||
lib/yang_wrappers.c \
|
||||
lib/zclient.c \
|
||||
lib/logicalrouter.c \
|
||||
lib/lua.c \
|
||||
|
@ -101,10 +107,17 @@ vtysh_scan += \
|
|||
# can be loaded as DSO - always include for vtysh
|
||||
vtysh_scan += $(top_srcdir)/lib/agentx.c
|
||||
|
||||
if SQLITE3
|
||||
lib_libfrr_la_LIBADD += -lsqlite3
|
||||
lib_libfrr_la_SOURCES += lib/db.c
|
||||
endif
|
||||
|
||||
lib/plist_clippy.c: $(CLIPPY_DEPS)
|
||||
lib/plist.lo: lib/plist_clippy.c
|
||||
lib/nexthop_group_clippy.c: $(CLIPPY_DEPS)
|
||||
lib/nexthop_group.lo: lib/nexthop_group_clippy.c
|
||||
lib/northbound_cli_clippy.c: $(CLIPPY_DEPS)
|
||||
lib/northbound_cli.lo: lib/northbound_cli_clippy.c
|
||||
|
||||
pkginclude_HEADERS += \
|
||||
lib/agg_table.h \
|
||||
|
@ -117,6 +130,7 @@ pkginclude_HEADERS += \
|
|||
lib/command_match.h \
|
||||
lib/compiler.h \
|
||||
lib/csv.h \
|
||||
lib/db.h \
|
||||
lib/debug.h \
|
||||
lib/distribute.h \
|
||||
lib/event_counter.h \
|
||||
|
@ -152,6 +166,9 @@ pkginclude_HEADERS += \
|
|||
lib/network.h \
|
||||
lib/nexthop.h \
|
||||
lib/nexthop_group.h \
|
||||
lib/northbound.h \
|
||||
lib/northbound_cli.h \
|
||||
lib/northbound_db.h \
|
||||
lib/ns.h \
|
||||
lib/openbsd-queue.h \
|
||||
lib/openbsd-tree.h \
|
||||
|
@ -187,6 +204,9 @@ pkginclude_HEADERS += \
|
|||
lib/vxlan.h \
|
||||
lib/wheel.h \
|
||||
lib/workqueue.h \
|
||||
lib/yang.h \
|
||||
lib/yang_translator.h \
|
||||
lib/yang_wrappers.h \
|
||||
lib/zassert.h \
|
||||
lib/zclient.h \
|
||||
lib/zebra.h \
|
||||
|
|
81
lib/vty.c
81
lib/vty.c
|
@ -41,6 +41,7 @@
|
|||
#include "libfrr.h"
|
||||
#include "frrstr.h"
|
||||
#include "lib_errors.h"
|
||||
#include "northbound_cli.h"
|
||||
|
||||
#include <arpa/telnet.h>
|
||||
#include <termios.h>
|
||||
|
@ -89,6 +90,9 @@ char *vty_cwd = NULL;
|
|||
static int vty_config;
|
||||
static int vty_config_is_lockless = 0;
|
||||
|
||||
/* Exclusive configuration lock. */
|
||||
struct vty *vty_exclusive_lock;
|
||||
|
||||
/* Login password check. */
|
||||
static int no_password_check = 0;
|
||||
|
||||
|
@ -828,6 +832,8 @@ static void vty_end_config(struct vty *vty)
|
|||
break;
|
||||
}
|
||||
|
||||
vty->xpath_index = 0;
|
||||
|
||||
vty_prompt(vty);
|
||||
vty->cp = 0;
|
||||
}
|
||||
|
@ -1718,6 +1724,10 @@ static struct vty *vty_new_init(int vty_sock)
|
|||
memset(vty->hist, 0, sizeof(vty->hist));
|
||||
vty->hp = 0;
|
||||
vty->hindex = 0;
|
||||
vty->xpath_index = 0;
|
||||
memset(vty->xpath, 0, sizeof(vty->xpath));
|
||||
vty->private_config = false;
|
||||
vty->candidate_config = vty_shared_candidate_config;
|
||||
vector_set_index(vtyvec, vty_sock, vty);
|
||||
vty->status = VTY_NORMAL;
|
||||
vty->lines = -1;
|
||||
|
@ -2372,7 +2382,7 @@ static int vty_timeout(struct thread *thread)
|
|||
}
|
||||
|
||||
/* Read up configuration file from file_name. */
|
||||
static void vty_read_file(FILE *confp)
|
||||
static void vty_read_file(struct nb_config *config, FILE *confp)
|
||||
{
|
||||
int ret;
|
||||
struct vty *vty;
|
||||
|
@ -2391,6 +2401,12 @@ static void vty_read_file(FILE *confp)
|
|||
vty->wfd = STDERR_FILENO;
|
||||
vty->type = VTY_FILE;
|
||||
vty->node = CONFIG_NODE;
|
||||
if (config)
|
||||
vty->candidate_config = config;
|
||||
else {
|
||||
vty->private_config = true;
|
||||
vty->candidate_config = nb_config_new(NULL);
|
||||
}
|
||||
|
||||
/* Execute configuration file */
|
||||
ret = config_from_file(vty, confp, &line_num);
|
||||
|
@ -2436,6 +2452,22 @@ static void vty_read_file(FILE *confp)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Automatically commit the candidate configuration after
|
||||
* reading the configuration file.
|
||||
*/
|
||||
if (config == NULL && vty->candidate_config
|
||||
&& frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
|
||||
int ret;
|
||||
|
||||
ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
|
||||
true, "Read configuration file",
|
||||
NULL);
|
||||
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
|
||||
zlog_err("%s: failed to read configuration file.",
|
||||
__func__);
|
||||
}
|
||||
|
||||
vty_close(vty);
|
||||
}
|
||||
|
||||
|
@ -2494,7 +2526,8 @@ static FILE *vty_use_backup_config(const char *fullpath)
|
|||
}
|
||||
|
||||
/* Read up configuration file from file_name. */
|
||||
bool vty_read_config(const char *config_file, char *config_default_dir)
|
||||
bool vty_read_config(struct nb_config *config, const char *config_file,
|
||||
char *config_default_dir)
|
||||
{
|
||||
char cwd[MAXPATHLEN];
|
||||
FILE *confp = NULL;
|
||||
|
@ -2508,9 +2541,9 @@ bool vty_read_config(const char *config_file, char *config_default_dir)
|
|||
if (getcwd(cwd, MAXPATHLEN) == NULL) {
|
||||
flog_err_sys(
|
||||
EC_LIB_SYSTEM_CALL,
|
||||
"Failure to determine Current Working Directory %d!",
|
||||
errno);
|
||||
exit(1);
|
||||
"%s: failure to determine Current Working Directory %d!",
|
||||
__func__, errno);
|
||||
goto tmp_free_and_out;
|
||||
}
|
||||
tmp = XMALLOC(MTYPE_TMP,
|
||||
strlen(cwd) + strlen(config_file) + 2);
|
||||
|
@ -2533,10 +2566,11 @@ bool vty_read_config(const char *config_file, char *config_default_dir)
|
|||
EC_LIB_BACKUP_CONFIG,
|
||||
"WARNING: using backup configuration file!");
|
||||
else {
|
||||
flog_err(EC_LIB_VTY,
|
||||
"can't open configuration file [%s]",
|
||||
config_file);
|
||||
exit(1);
|
||||
flog_err(
|
||||
EC_LIB_VTY,
|
||||
"%s: can't open configuration file [%s]",
|
||||
__func__, config_file);
|
||||
goto tmp_free_and_out;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2593,7 +2627,7 @@ bool vty_read_config(const char *config_file, char *config_default_dir)
|
|||
fullpath = config_default_dir;
|
||||
}
|
||||
|
||||
vty_read_file(confp);
|
||||
vty_read_file(config, confp);
|
||||
read_success = true;
|
||||
|
||||
fclose(confp);
|
||||
|
@ -2671,6 +2705,18 @@ int vty_config_lock(struct vty *vty)
|
|||
|
||||
int vty_config_unlock(struct vty *vty)
|
||||
{
|
||||
vty_config_exclusive_unlock(vty);
|
||||
|
||||
if (vty->candidate_config) {
|
||||
if (vty->private_config)
|
||||
nb_config_free(vty->candidate_config);
|
||||
vty->candidate_config = NULL;
|
||||
}
|
||||
if (vty->candidate_config_base) {
|
||||
nb_config_free(vty->candidate_config_base);
|
||||
vty->candidate_config_base = NULL;
|
||||
}
|
||||
|
||||
if (vty_config_is_lockless)
|
||||
return 0;
|
||||
if (vty_config == 1 && vty->config == 1) {
|
||||
|
@ -2685,6 +2731,21 @@ void vty_config_lockless(void)
|
|||
vty_config_is_lockless = 1;
|
||||
}
|
||||
|
||||
int vty_config_exclusive_lock(struct vty *vty)
|
||||
{
|
||||
if (vty_exclusive_lock == NULL) {
|
||||
vty_exclusive_lock = vty;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vty_config_exclusive_unlock(struct vty *vty)
|
||||
{
|
||||
if (vty_exclusive_lock == vty)
|
||||
vty_exclusive_lock = NULL;
|
||||
}
|
||||
|
||||
/* Master of the threads. */
|
||||
static struct thread_master *vty_master;
|
||||
|
||||
|
|
49
lib/vty.h
49
lib/vty.h
|
@ -29,9 +29,11 @@
|
|||
#include "sockunion.h"
|
||||
#include "qobj.h"
|
||||
#include "compiler.h"
|
||||
#include "northbound.h"
|
||||
|
||||
#define VTY_BUFSIZ 4096
|
||||
#define VTY_MAXHIST 20
|
||||
#define VTY_MAXDEPTH 8
|
||||
|
||||
struct vty_error {
|
||||
char error_buf[VTY_BUFSIZ];
|
||||
|
@ -96,6 +98,19 @@ struct vty {
|
|||
/* History insert end point */
|
||||
int hindex;
|
||||
|
||||
/* XPath of the current node */
|
||||
int xpath_index;
|
||||
char xpath[VTY_MAXDEPTH][XPATH_MAXLEN];
|
||||
|
||||
/* Private candidate configuration mode. */
|
||||
bool private_config;
|
||||
|
||||
/* Candidate configuration. */
|
||||
struct nb_config *candidate_config;
|
||||
|
||||
/* Base candidate configuration. */
|
||||
struct nb_config *candidate_config_base;
|
||||
|
||||
/* qobj object ID (replacement for "index") */
|
||||
uint64_t qobj_index;
|
||||
|
||||
|
@ -201,6 +216,34 @@ static inline void vty_push_context(struct vty *vty, int node, uint64_t id)
|
|||
struct structname *ptr = VTY_GET_CONTEXT(structname); \
|
||||
VTY_CHECK_CONTEXT(ptr);
|
||||
|
||||
/* XPath macros. */
|
||||
#define VTY_PUSH_XPATH(nodeval, value) \
|
||||
do { \
|
||||
if (vty->xpath_index >= VTY_MAXDEPTH) { \
|
||||
vty_out(vty, "%% Reached maximum CLI depth (%u)\n", \
|
||||
VTY_MAXDEPTH); \
|
||||
return CMD_WARNING; \
|
||||
} \
|
||||
vty->node = nodeval; \
|
||||
strlcpy(vty->xpath[vty->xpath_index], value, \
|
||||
sizeof(vty->xpath[0])); \
|
||||
vty->xpath_index++; \
|
||||
} while (0)
|
||||
|
||||
#define VTY_CURR_XPATH vty->xpath[vty->xpath_index - 1]
|
||||
|
||||
#define VTY_CHECK_XPATH \
|
||||
do { \
|
||||
if (vty->xpath_index > 0 \
|
||||
&& !yang_dnode_exists(vty->candidate_config->dnode, \
|
||||
VTY_CURR_XPATH)) { \
|
||||
vty_out(vty, \
|
||||
"Current configuration object was deleted " \
|
||||
"by another process.\n\n"); \
|
||||
return CMD_WARNING; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct vty_arg {
|
||||
const char *name;
|
||||
const char *value;
|
||||
|
@ -228,6 +271,7 @@ struct vty_arg {
|
|||
|
||||
/* Exported variables */
|
||||
extern char integrate_default[];
|
||||
extern struct vty *vty_exclusive_lock;
|
||||
|
||||
/* Prototypes. */
|
||||
extern void vty_init(struct thread_master *);
|
||||
|
@ -247,7 +291,8 @@ extern void vty_frame(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
|
|||
extern void vty_endframe(struct vty *, const char *);
|
||||
bool vty_set_include(struct vty *vty, const char *regexp);
|
||||
|
||||
extern bool vty_read_config(const char *, char *);
|
||||
extern bool vty_read_config(struct nb_config *config, const char *config_file,
|
||||
char *config_default_dir);
|
||||
extern void vty_time_print(struct vty *, int);
|
||||
extern void vty_serv_sock(const char *, unsigned short, const char *);
|
||||
extern void vty_close(struct vty *);
|
||||
|
@ -257,6 +302,8 @@ extern void vty_log(const char *level, const char *proto, const char *fmt,
|
|||
extern int vty_config_lock(struct vty *);
|
||||
extern int vty_config_unlock(struct vty *);
|
||||
extern void vty_config_lockless(void);
|
||||
extern int vty_config_exclusive_lock(struct vty *vty);
|
||||
extern void vty_config_exclusive_unlock(struct vty *vty);
|
||||
extern int vty_shell(struct vty *);
|
||||
extern int vty_shell_serv(struct vty *);
|
||||
extern void vty_hello(struct vty *);
|
||||
|
|
618
lib/yang.c
Normal file
618
lib/yang.c
Normal file
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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 "log.h"
|
||||
#include "log_int.h"
|
||||
#include "lib_errors.h"
|
||||
#include "yang.h"
|
||||
#include "yang_translator.h"
|
||||
#include "northbound.h"
|
||||
|
||||
DEFINE_MTYPE(LIB, YANG_MODULE, "YANG module")
|
||||
DEFINE_MTYPE(LIB, YANG_DATA, "YANG data structure")
|
||||
|
||||
/* libyang container. */
|
||||
struct ly_ctx *ly_native_ctx;
|
||||
|
||||
/* Generate the yang_modules tree. */
|
||||
static inline int yang_module_compare(const struct yang_module *a,
|
||||
const struct yang_module *b)
|
||||
{
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare)
|
||||
|
||||
struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules);
|
||||
|
||||
struct yang_module *yang_module_load(const char *module_name)
|
||||
{
|
||||
struct yang_module *module;
|
||||
const struct lys_module *module_info;
|
||||
|
||||
module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL);
|
||||
if (!module_info) {
|
||||
flog_err(EC_LIB_YANG_MODULE_LOAD,
|
||||
"%s: failed to load data model: %s", __func__,
|
||||
module_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
module = XCALLOC(MTYPE_YANG_MODULE, sizeof(*module));
|
||||
module->name = module_name;
|
||||
module->info = module_info;
|
||||
|
||||
if (RB_INSERT(yang_modules, &yang_modules, module) != NULL) {
|
||||
flog_err(EC_LIB_YANG_MODULE_LOADED_ALREADY,
|
||||
"%s: YANG module is loaded already: %s", __func__,
|
||||
module_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
struct yang_module *yang_module_find(const char *module_name)
|
||||
{
|
||||
struct yang_module s;
|
||||
|
||||
s.name = module_name;
|
||||
return RB_FIND(yang_modules, &yang_modules, &s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for yang_module_snodes_iterate() and
|
||||
* yang_all_snodes_iterate(). This is a recursive function.
|
||||
*/
|
||||
static void yang_snodes_iterate(const struct lys_node *snode,
|
||||
void (*func)(const struct lys_node *, void *,
|
||||
void *),
|
||||
uint16_t flags, void *arg1, void *arg2)
|
||||
{
|
||||
struct lys_node *child;
|
||||
|
||||
if (CHECK_FLAG(flags, YANG_ITER_FILTER_IMPLICIT)) {
|
||||
switch (snode->nodetype) {
|
||||
case LYS_CASE:
|
||||
case LYS_INPUT:
|
||||
case LYS_OUTPUT:
|
||||
if (snode->flags & LYS_IMPLICIT)
|
||||
goto next;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (snode->nodetype) {
|
||||
case LYS_CONTAINER:
|
||||
if (CHECK_FLAG(flags, YANG_ITER_FILTER_NPCONTAINERS)) {
|
||||
struct lys_node_container *scontainer;
|
||||
|
||||
scontainer = (struct lys_node_container *)snode;
|
||||
if (!scontainer->presence)
|
||||
goto next;
|
||||
}
|
||||
break;
|
||||
case LYS_LEAF:
|
||||
if (CHECK_FLAG(flags, YANG_ITER_FILTER_LIST_KEYS)) {
|
||||
struct lys_node_leaf *sleaf;
|
||||
|
||||
/* Ignore list keys. */
|
||||
sleaf = (struct lys_node_leaf *)snode;
|
||||
if (lys_is_key(sleaf, NULL))
|
||||
goto next;
|
||||
}
|
||||
break;
|
||||
case LYS_GROUPING:
|
||||
/* Return since we're not interested in the grouping subtree. */
|
||||
return;
|
||||
case LYS_USES:
|
||||
case LYS_AUGMENT:
|
||||
/* Always ignore nodes of these types. */
|
||||
goto next;
|
||||
case LYS_INPUT:
|
||||
case LYS_OUTPUT:
|
||||
if (CHECK_FLAG(flags, YANG_ITER_FILTER_INPUT_OUTPUT))
|
||||
goto next;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
(*func)(snode, arg1, arg2);
|
||||
|
||||
next:
|
||||
/*
|
||||
* YANG leafs and leaf-lists can't have child nodes, and trying to
|
||||
* access snode->child is undefined behavior.
|
||||
*/
|
||||
if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST))
|
||||
return;
|
||||
|
||||
LY_TREE_FOR (snode->child, child) {
|
||||
if (child->parent != snode)
|
||||
continue;
|
||||
yang_snodes_iterate(child, func, flags, arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
void yang_module_snodes_iterate(const struct lys_module *module,
|
||||
void (*func)(const struct lys_node *, void *,
|
||||
void *),
|
||||
uint16_t flags, void *arg1, void *arg2)
|
||||
{
|
||||
struct lys_node *snode;
|
||||
|
||||
LY_TREE_FOR (module->data, snode) {
|
||||
yang_snodes_iterate(snode, func, flags, arg1, arg2);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < module->augment_size; i++) {
|
||||
yang_snodes_iterate(
|
||||
(const struct lys_node *)&module->augment[i], func,
|
||||
flags, arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
void yang_all_snodes_iterate(void (*func)(const struct lys_node *, void *,
|
||||
void *),
|
||||
uint16_t flags, void *arg1, void *arg2)
|
||||
{
|
||||
struct yang_module *module;
|
||||
|
||||
RB_FOREACH (module, yang_modules, &yang_modules)
|
||||
yang_module_snodes_iterate(module->info, func, flags, arg1,
|
||||
arg2);
|
||||
}
|
||||
|
||||
void yang_snode_get_path(const struct lys_node *snode, enum yang_path_type type,
|
||||
char *xpath, size_t xpath_len)
|
||||
{
|
||||
char *xpath_ptr;
|
||||
|
||||
switch (type) {
|
||||
case YANG_PATH_SCHEMA:
|
||||
xpath_ptr = lys_path(snode, 0);
|
||||
break;
|
||||
case YANG_PATH_DATA:
|
||||
xpath_ptr = lys_data_path(snode);
|
||||
break;
|
||||
default:
|
||||
flog_err(EC_LIB_DEVELOPMENT, "%s: unknown yang path type: %u",
|
||||
__func__, type);
|
||||
exit(1);
|
||||
}
|
||||
strlcpy(xpath, xpath_ptr, xpath_len);
|
||||
free(xpath_ptr);
|
||||
}
|
||||
|
||||
struct lys_node *yang_snode_real_parent(const struct lys_node *snode)
|
||||
{
|
||||
struct lys_node *parent = snode->parent;
|
||||
|
||||
while (parent) {
|
||||
struct lys_node_container *scontainer;
|
||||
|
||||
switch (parent->nodetype) {
|
||||
case LYS_CONTAINER:
|
||||
scontainer = (struct lys_node_container *)parent;
|
||||
if (scontainer->presence)
|
||||
return parent;
|
||||
break;
|
||||
case LYS_LIST:
|
||||
return parent;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
parent = parent->parent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lys_node *yang_snode_parent_list(const struct lys_node *snode)
|
||||
{
|
||||
struct lys_node *parent = snode->parent;
|
||||
|
||||
while (parent) {
|
||||
switch (parent->nodetype) {
|
||||
case LYS_LIST:
|
||||
return parent;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
parent = parent->parent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool yang_snode_is_typeless_data(const struct lys_node *snode)
|
||||
{
|
||||
struct lys_node_leaf *sleaf;
|
||||
|
||||
switch (snode->nodetype) {
|
||||
case LYS_LEAF:
|
||||
sleaf = (struct lys_node_leaf *)snode;
|
||||
if (sleaf->type.base == LY_TYPE_EMPTY)
|
||||
return true;
|
||||
return false;
|
||||
case LYS_LEAFLIST:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const char *yang_snode_get_default(const struct lys_node *snode)
|
||||
{
|
||||
struct lys_node_leaf *sleaf;
|
||||
|
||||
switch (snode->nodetype) {
|
||||
case LYS_LEAF:
|
||||
sleaf = (struct lys_node_leaf *)snode;
|
||||
|
||||
/* NOTE: this might be null. */
|
||||
return sleaf->dflt;
|
||||
case LYS_LEAFLIST:
|
||||
/* TODO: check leaf-list default values */
|
||||
return NULL;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const struct lys_type *yang_snode_get_type(const struct lys_node *snode)
|
||||
{
|
||||
struct lys_node_leaf *sleaf = (struct lys_node_leaf *)snode;
|
||||
struct lys_type *type;
|
||||
|
||||
if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)))
|
||||
return NULL;
|
||||
|
||||
type = &sleaf->type;
|
||||
while (type->base == LY_TYPE_LEAFREF)
|
||||
type = &type->info.lref.target->type;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
|
||||
size_t xpath_len)
|
||||
{
|
||||
char *xpath_ptr;
|
||||
|
||||
xpath_ptr = lyd_path(dnode);
|
||||
strlcpy(xpath, xpath_ptr, xpath_len);
|
||||
free(xpath_ptr);
|
||||
}
|
||||
|
||||
struct lyd_node *yang_dnode_get(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
struct ly_set *set;
|
||||
struct lyd_node *dnode_ret = NULL;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
set = lyd_find_path(dnode, xpath);
|
||||
assert(set);
|
||||
if (set->number == 0)
|
||||
goto exit;
|
||||
|
||||
if (set->number > 1) {
|
||||
flog_warn(EC_LIB_YANG_DNODE_NOT_FOUND,
|
||||
"%s: found %u elements (expected 0 or 1) [xpath %s]",
|
||||
__func__, set->number, xpath);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dnode_ret = set->set.d[0];
|
||||
|
||||
exit:
|
||||
ly_set_free(set);
|
||||
|
||||
return dnode_ret;
|
||||
}
|
||||
|
||||
bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
struct ly_set *set;
|
||||
bool found;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
set = lyd_find_path(dnode, xpath);
|
||||
assert(set);
|
||||
found = (set->number > 0);
|
||||
ly_set_free(set);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath_fmt,
|
||||
...)
|
||||
{
|
||||
struct lys_node *snode;
|
||||
struct lys_node_leaf *sleaf;
|
||||
struct lys_node_container *scontainer;
|
||||
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
}
|
||||
|
||||
assert(dnode);
|
||||
snode = dnode->schema;
|
||||
switch (snode->nodetype) {
|
||||
case LYS_LEAF:
|
||||
sleaf = (struct lys_node_leaf *)snode;
|
||||
if (sleaf->type.base == LY_TYPE_EMPTY)
|
||||
return false;
|
||||
return lyd_wd_default((struct lyd_node_leaf_list *)dnode);
|
||||
case LYS_LEAFLIST:
|
||||
/* TODO: check leaf-list default values */
|
||||
return false;
|
||||
case LYS_CONTAINER:
|
||||
scontainer = (struct lys_node_container *)snode;
|
||||
if (scontainer->presence)
|
||||
return false;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool yang_dnode_is_default_recursive(const struct lyd_node *dnode)
|
||||
{
|
||||
struct lys_node *snode;
|
||||
struct lyd_node *root, *next, *dnode_iter;
|
||||
|
||||
snode = dnode->schema;
|
||||
if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST))
|
||||
return yang_dnode_is_default(dnode, NULL);
|
||||
|
||||
if (!yang_dnode_is_default(dnode, NULL))
|
||||
return false;
|
||||
|
||||
LY_TREE_FOR (dnode->child, root) {
|
||||
LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
|
||||
if (!yang_dnode_is_default(dnode_iter, NULL))
|
||||
return false;
|
||||
|
||||
LY_TREE_DFS_END(root, next, dnode_iter);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value)
|
||||
{
|
||||
assert(dnode->schema->nodetype == LYS_LEAF);
|
||||
lyd_change_leaf((struct lyd_node_leaf_list *)dnode, value);
|
||||
}
|
||||
|
||||
void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry)
|
||||
{
|
||||
assert(dnode->schema->nodetype & (LYS_LIST | LYS_CONTAINER));
|
||||
lyd_set_private(dnode, entry);
|
||||
}
|
||||
|
||||
void *yang_dnode_get_entry(const struct lyd_node *dnode)
|
||||
{
|
||||
const struct lyd_node *orig_dnode = dnode;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
while (dnode) {
|
||||
switch (dnode->schema->nodetype) {
|
||||
case LYS_CONTAINER:
|
||||
case LYS_LIST:
|
||||
if (dnode->priv)
|
||||
return dnode->priv;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dnode = dnode->parent;
|
||||
}
|
||||
|
||||
yang_dnode_get_path(orig_dnode, xpath, sizeof(xpath));
|
||||
flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
|
||||
"%s: failed to find entry [xpath %s]", __func__, xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx)
|
||||
{
|
||||
struct lyd_node *dnode;
|
||||
|
||||
dnode = NULL;
|
||||
if (lyd_validate(&dnode, LYD_OPT_CONFIG, ly_ctx) != 0) {
|
||||
/* Should never happen. */
|
||||
flog_err(EC_LIB_LIBYANG, "%s: lyd_validate() failed", __func__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return dnode;
|
||||
}
|
||||
|
||||
struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode)
|
||||
{
|
||||
return lyd_dup_withsiblings(dnode, 1);
|
||||
}
|
||||
|
||||
void yang_dnode_free(struct lyd_node *dnode)
|
||||
{
|
||||
lyd_free_withsiblings(dnode);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new(const char *xpath, const char *value)
|
||||
{
|
||||
const struct lys_node *snode;
|
||||
struct yang_data *data;
|
||||
|
||||
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
|
||||
if (!snode)
|
||||
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 1);
|
||||
if (!snode) {
|
||||
flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
||||
"%s: unknown data path: %s", __func__, xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
data = XCALLOC(MTYPE_YANG_DATA, sizeof(*data));
|
||||
strlcpy(data->xpath, xpath, sizeof(data->xpath));
|
||||
data->snode = snode;
|
||||
if (value)
|
||||
data->value = strdup(value);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void yang_data_free(struct yang_data *data)
|
||||
{
|
||||
if (data->value)
|
||||
free(data->value);
|
||||
XFREE(MTYPE_YANG_DATA, data);
|
||||
}
|
||||
|
||||
struct list *yang_data_list_new(void)
|
||||
{
|
||||
struct list *list;
|
||||
|
||||
list = list_new();
|
||||
list->del = (void (*)(void *))yang_data_free;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void *ly_dup_cb(const void *priv)
|
||||
{
|
||||
/* Make a shallow copy of the priv pointer. */
|
||||
return (void *)priv;
|
||||
}
|
||||
|
||||
/* Make libyang log its errors using FRR logging infrastructure. */
|
||||
static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
|
||||
{
|
||||
int priority;
|
||||
|
||||
switch (level) {
|
||||
case LY_LLERR:
|
||||
priority = LOG_ERR;
|
||||
break;
|
||||
case LY_LLWRN:
|
||||
priority = LOG_WARNING;
|
||||
break;
|
||||
case LY_LLVRB:
|
||||
priority = LOG_DEBUG;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (path)
|
||||
zlog(priority, "libyang: %s (%s)", msg, path);
|
||||
else
|
||||
zlog(priority, "libyang: %s", msg);
|
||||
}
|
||||
|
||||
void yang_init(void)
|
||||
{
|
||||
static char ly_plugin_dir[PATH_MAX];
|
||||
const char *const *ly_loaded_plugins;
|
||||
const char *ly_plugin;
|
||||
bool found_ly_frr_types = false;
|
||||
|
||||
/* Tell libyang where to find its plugins. */
|
||||
snprintf(ly_plugin_dir, sizeof(ly_plugin_dir), "%s=%s",
|
||||
"LIBYANG_USER_TYPES_PLUGINS_DIR", LIBYANG_PLUGINS_PATH);
|
||||
putenv(ly_plugin_dir);
|
||||
|
||||
/* Initialize libyang global parameters that affect all containers. */
|
||||
ly_set_log_clb(ly_log_cb, 1);
|
||||
ly_log_options(LY_LOLOG | LY_LOSTORE);
|
||||
|
||||
/* Initialize libyang container for native models. */
|
||||
ly_native_ctx = ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD);
|
||||
if (!ly_native_ctx) {
|
||||
flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
|
||||
exit(1);
|
||||
}
|
||||
ly_ctx_set_searchdir(ly_native_ctx, YANG_MODELS_PATH);
|
||||
ly_ctx_set_priv_dup_clb(ly_native_ctx, ly_dup_cb);
|
||||
|
||||
/* Detect if the required libyang plugin(s) were loaded successfully. */
|
||||
ly_loaded_plugins = ly_get_loaded_plugins();
|
||||
for (size_t i = 0; (ly_plugin = ly_loaded_plugins[i]); i++) {
|
||||
if (strmatch(ly_plugin, "frr_user_types")) {
|
||||
found_ly_frr_types = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_ly_frr_types) {
|
||||
flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD,
|
||||
"%s: failed to load frr_user_types.so", __func__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
yang_translator_init();
|
||||
}
|
||||
|
||||
void yang_terminate(void)
|
||||
{
|
||||
struct yang_module *module;
|
||||
|
||||
yang_translator_terminate();
|
||||
|
||||
while (!RB_EMPTY(yang_modules, &yang_modules)) {
|
||||
module = RB_ROOT(yang_modules, &yang_modules);
|
||||
|
||||
/*
|
||||
* We shouldn't call ly_ctx_remove_module() here because this
|
||||
* function also removes other modules that depend on it.
|
||||
*
|
||||
* ly_ctx_destroy() will release all memory for us.
|
||||
*/
|
||||
RB_REMOVE(yang_modules, &yang_modules, module);
|
||||
XFREE(MTYPE_YANG_MODULE, module);
|
||||
}
|
||||
|
||||
ly_ctx_destroy(ly_native_ctx, NULL);
|
||||
}
|
439
lib/yang.h
Normal file
439
lib/yang.h
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _FRR_YANG_H_
|
||||
#define _FRR_YANG_H_
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include <libyang/libyang.h>
|
||||
#ifdef HAVE_SYSREPO
|
||||
#include <sysrepo.h>
|
||||
#endif
|
||||
|
||||
#include "yang_wrappers.h"
|
||||
|
||||
DECLARE_MTYPE(YANG_MODULE)
|
||||
DECLARE_MTYPE(YANG_DATA)
|
||||
|
||||
/* Maximum XPath length. */
|
||||
#define XPATH_MAXLEN 256
|
||||
|
||||
/* Maximum list key length. */
|
||||
#define LIST_MAXKEYS 8
|
||||
|
||||
/* Maximum list key length. */
|
||||
#define LIST_MAXKEYLEN 128
|
||||
|
||||
/* Maximum string length of an YANG value. */
|
||||
#define YANG_VALUE_MAXLEN 1024
|
||||
|
||||
struct yang_module {
|
||||
RB_ENTRY(yang_module) entry;
|
||||
const char *name;
|
||||
const struct lys_module *info;
|
||||
#ifdef HAVE_CONFD
|
||||
int confd_hash;
|
||||
#endif
|
||||
#ifdef HAVE_SYSREPO
|
||||
sr_subscription_ctx_t *sr_subscription;
|
||||
#endif
|
||||
};
|
||||
RB_HEAD(yang_modules, yang_module);
|
||||
RB_PROTOTYPE(yang_modules, yang_module, entry, yang_module_compare);
|
||||
|
||||
struct yang_data {
|
||||
/* XPath identifier of the data element. */
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
/*
|
||||
* Schema information (necessary to interpret certain values like
|
||||
* enums).
|
||||
*/
|
||||
const struct lys_node *snode;
|
||||
|
||||
/* Value encoded as a raw string. */
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct yang_list_keys {
|
||||
/* Number os keys (max: LIST_MAXKEYS). */
|
||||
uint8_t num;
|
||||
|
||||
struct {
|
||||
/*
|
||||
* Schema information (necessary to interpret certain values
|
||||
* like enums).
|
||||
*/
|
||||
struct lys_node *snode;
|
||||
|
||||
/* Value encoded as a raw string. */
|
||||
char value[LIST_MAXKEYLEN];
|
||||
} key[LIST_MAXKEYS];
|
||||
};
|
||||
|
||||
enum yang_path_type {
|
||||
YANG_PATH_SCHEMA = 0,
|
||||
YANG_PATH_DATA,
|
||||
};
|
||||
|
||||
/* Filter non-presence containers. */
|
||||
#define YANG_ITER_FILTER_NPCONTAINERS 0x0001
|
||||
/* Filter list keys (leafs). */
|
||||
#define YANG_ITER_FILTER_LIST_KEYS 0x0002
|
||||
/* Filter RPC input/output nodes. */
|
||||
#define YANG_ITER_FILTER_INPUT_OUTPUT 0x0004
|
||||
/* Filter implicitely created nodes. */
|
||||
#define YANG_ITER_FILTER_IMPLICIT 0x0008
|
||||
|
||||
/* Global libyang context for native FRR models. */
|
||||
extern struct ly_ctx *ly_native_ctx;
|
||||
|
||||
/* Tree of all loaded YANG modules. */
|
||||
extern struct yang_modules yang_modules;
|
||||
|
||||
/*
|
||||
* Create a new YANG module and load it using libyang. If the YANG module is not
|
||||
* found in the YANG_MODELS_PATH directory, the program will exit with an error.
|
||||
* Once loaded, a YANG module can't be unloaded anymore.
|
||||
*
|
||||
* module_name
|
||||
* Name of the YANG module.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created YANG module.
|
||||
*/
|
||||
extern struct yang_module *yang_module_load(const char *module_name);
|
||||
|
||||
/*
|
||||
* Find a YANG module by its name.
|
||||
*
|
||||
* module_name
|
||||
* Name of the YANG module.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to YANG module if found, NULL otherwise.
|
||||
*/
|
||||
extern struct yang_module *yang_module_find(const char *module_name);
|
||||
|
||||
/*
|
||||
* Iterate over all libyang schema nodes from the given YANG module.
|
||||
*
|
||||
* module
|
||||
* YANG module to operate on.
|
||||
*
|
||||
* func
|
||||
* Function to call with each schema node.
|
||||
*
|
||||
* flags
|
||||
* YANG_ITER_FILTER_* flags to specify node types that should be filtered.
|
||||
*
|
||||
* arg1
|
||||
* Arbitrary argument passed as the second parameter in each call to 'func'.
|
||||
*
|
||||
* arg2
|
||||
* Arbitrary argument passed as the third parameter in each call to 'func'.
|
||||
*/
|
||||
extern void yang_module_snodes_iterate(const struct lys_module *module,
|
||||
void (*func)(const struct lys_node *,
|
||||
void *, void *),
|
||||
uint16_t flags, void *arg1, void *arg2);
|
||||
|
||||
/*
|
||||
* Iterate over all libyang schema nodes from all loaded YANG modules.
|
||||
*
|
||||
* func
|
||||
* Function to call with each schema node.
|
||||
*
|
||||
* flags
|
||||
* YANG_ITER_FILTER_* flags to specify node types that should be filtered.
|
||||
*
|
||||
* arg1
|
||||
* Arbitrary argument passed as the second parameter in each call to 'func'.
|
||||
*
|
||||
* arg2
|
||||
* Arbitrary argument passed as the third parameter in each call to 'func'.
|
||||
*/
|
||||
extern void yang_all_snodes_iterate(void (*func)(const struct lys_node *,
|
||||
void *, void *),
|
||||
uint16_t flags, void *arg1, void *arg2);
|
||||
|
||||
/*
|
||||
* Build schema path or data path of the schema node.
|
||||
*
|
||||
* snode
|
||||
* libyang schema node to be processed.
|
||||
*
|
||||
* type
|
||||
* Specify whether a schema path or a data path should be built.
|
||||
*
|
||||
* xpath
|
||||
* Pointer to previously allocated buffer.
|
||||
*
|
||||
* xpath_len
|
||||
* Size of the xpath buffer.
|
||||
*/
|
||||
extern void yang_snode_get_path(const struct lys_node *snode,
|
||||
enum yang_path_type type, char *xpath,
|
||||
size_t xpath_len);
|
||||
|
||||
/*
|
||||
* Find first parent schema node which is a presence-container or a list
|
||||
* (non-presence containers are ignored).
|
||||
*
|
||||
* snode
|
||||
* libyang schema node to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* The parent libyang schema node if found, or NULL if not found.
|
||||
*/
|
||||
extern struct lys_node *yang_snode_real_parent(const struct lys_node *snode);
|
||||
|
||||
/*
|
||||
* Find first parent schema node which is a list.
|
||||
*
|
||||
* snode
|
||||
* libyang schema node to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* The parent libyang schema node (list) if found, or NULL if not found.
|
||||
*/
|
||||
extern struct lys_node *yang_snode_parent_list(const struct lys_node *snode);
|
||||
|
||||
/*
|
||||
* Check if the libyang schema node represents typeless data (e.g. containers,
|
||||
* leafs of type empty, etc).
|
||||
*
|
||||
* snode
|
||||
* libyang schema node to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* true if the schema node represents typeless data, false otherwise.
|
||||
*/
|
||||
extern bool yang_snode_is_typeless_data(const struct lys_node *snode);
|
||||
|
||||
/*
|
||||
* Get the default value associated to a YANG leaf or leaf-list.
|
||||
*
|
||||
* snode
|
||||
* libyang schema node to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* The default value if it exists, NULL otherwise.
|
||||
*/
|
||||
extern const char *yang_snode_get_default(const struct lys_node *snode);
|
||||
|
||||
/*
|
||||
* Get the type structure of a leaf of leaf-list. If the type is a leafref, the
|
||||
* final (if there is a chain of leafrefs) target's type is found.
|
||||
*
|
||||
* snode
|
||||
* libyang schema node to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* The found type if the schema node represents a leaf or a leaf-list, NULL
|
||||
* otherwise.
|
||||
*/
|
||||
extern const struct lys_type *yang_snode_get_type(const struct lys_node *snode);
|
||||
|
||||
/*
|
||||
* Build data path of the data node.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node to be processed.
|
||||
*
|
||||
* xpath
|
||||
* Pointer to previously allocated buffer.
|
||||
*
|
||||
* xpath_len
|
||||
* Size of the xpath buffer.
|
||||
*/
|
||||
extern void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
|
||||
size_t xpath_len);
|
||||
|
||||
/*
|
||||
* Find a libyang data node by its YANG data path.
|
||||
*
|
||||
* dnode
|
||||
* Base libyang data node to operate on.
|
||||
*
|
||||
* xpath_fmt
|
||||
* XPath expression (absolute or relative).
|
||||
*
|
||||
* Returns:
|
||||
* The libyang data node if found, or NULL if not found.
|
||||
*/
|
||||
extern struct lyd_node *yang_dnode_get(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
|
||||
/*
|
||||
* Check if a libyang data node exists.
|
||||
*
|
||||
* dnode
|
||||
* Base libyang data node to operate on.
|
||||
*
|
||||
* xpath_fmt
|
||||
* XPath expression (absolute or relative).
|
||||
*
|
||||
* Returns:
|
||||
* true if the libyang data node was found, false otherwise.
|
||||
*/
|
||||
extern bool yang_dnode_exists(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
|
||||
/*
|
||||
* Check if the libyang data node contains a default value. Non-presence
|
||||
* containers are assumed to always contain a default value.
|
||||
*
|
||||
* dnode
|
||||
* Base libyang data node to operate on.
|
||||
*
|
||||
* xpath_fmt
|
||||
* Optional XPath expression (absolute or relative) to specify a different
|
||||
* data node to operate on in the same data tree.
|
||||
*
|
||||
* Returns:
|
||||
* true if the data node contains the default value, false otherwise.
|
||||
*/
|
||||
extern bool yang_dnode_is_default(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
|
||||
/*
|
||||
* Check if the libyang data node and all of its children contain default
|
||||
* values. Non-presence containers are assumed to always contain a default
|
||||
* value.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* true if the data node and all of its children contain default values,
|
||||
* false otherwise.
|
||||
*/
|
||||
extern bool yang_dnode_is_default_recursive(const struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Change the value of a libyang leaf node.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node to operate on.
|
||||
*
|
||||
* value
|
||||
* String representing the new value.
|
||||
*/
|
||||
extern void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value);
|
||||
|
||||
/*
|
||||
* Set the libyang private pointer to a user pointer. Can only be used on YANG
|
||||
* lists and containers.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node to operate on.
|
||||
*
|
||||
* entry
|
||||
* Arbitrary user-specified pointer.
|
||||
*/
|
||||
extern void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry);
|
||||
|
||||
/*
|
||||
* Find the closest data node that contains an user pointer and return it.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* User pointer if found, NULL otherwise.
|
||||
*/
|
||||
extern void *yang_dnode_get_entry(const struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Create a new libyang data node.
|
||||
*
|
||||
* ly_ctx
|
||||
* libyang context to operate on.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created libyang data node.
|
||||
*/
|
||||
extern struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx);
|
||||
|
||||
/*
|
||||
* Duplicate a libyang data node.
|
||||
*
|
||||
* dnode
|
||||
* libyang data node to duplicate.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to duplicated libyang data node.
|
||||
*/
|
||||
extern struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Delete a libyang data node.
|
||||
*
|
||||
* dnode
|
||||
* Pointer to the libyang data node that is going to be deleted.
|
||||
*/
|
||||
extern void yang_dnode_free(struct lyd_node *dnode);
|
||||
|
||||
/*
|
||||
* Create a new yang_data structure.
|
||||
*
|
||||
* xpath
|
||||
* Data path of the YANG data.
|
||||
*
|
||||
* value
|
||||
* String representing the value of the YANG data.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created yang_data structure.
|
||||
*/
|
||||
extern struct yang_data *yang_data_new(const char *xpath, const char *value);
|
||||
|
||||
/*
|
||||
* Delete a yang_data structure.
|
||||
*
|
||||
* data
|
||||
* yang_data to delete.
|
||||
*/
|
||||
extern void yang_data_free(struct yang_data *data);
|
||||
|
||||
/*
|
||||
* Create a new linked list of yang_data structures. The list 'del' callback is
|
||||
* initialized appropriately so that the entire list can be deleted safely with
|
||||
* list_delete_and_null().
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created linked list.
|
||||
*/
|
||||
extern struct list *yang_data_list_new(void);
|
||||
|
||||
/*
|
||||
* Initialize the YANG subsystem. Should be called only once during the
|
||||
* daemon initialization process.
|
||||
*/
|
||||
extern void yang_init(void);
|
||||
|
||||
/*
|
||||
* Finish the YANG subsystem gracefully. Should be called only when the daemon
|
||||
* is exiting.
|
||||
*/
|
||||
extern void yang_terminate(void);
|
||||
|
||||
#endif /* _FRR_YANG_H_ */
|
545
lib/yang_translator.c
Normal file
545
lib/yang_translator.c
Normal file
|
@ -0,0 +1,545 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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 "log.h"
|
||||
#include "lib_errors.h"
|
||||
#include "hash.h"
|
||||
#include "yang.h"
|
||||
#include "yang_translator.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR, "YANG Translator")
|
||||
DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MODULE, "YANG Translator Module")
|
||||
DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MAPPING, "YANG Translator Mapping")
|
||||
|
||||
/* Generate the yang_translators tree. */
|
||||
static inline int yang_translator_compare(const struct yang_translator *a,
|
||||
const struct yang_translator *b)
|
||||
{
|
||||
return strcmp(a->family, b->family);
|
||||
}
|
||||
RB_GENERATE(yang_translators, yang_translator, entry, yang_translator_compare)
|
||||
|
||||
struct yang_translators yang_translators = RB_INITIALIZER(&yang_translators);
|
||||
|
||||
/* Separate libyang context for the translator module. */
|
||||
static struct ly_ctx *ly_translator_ctx;
|
||||
|
||||
static unsigned int
|
||||
yang_translator_validate(struct yang_translator *translator);
|
||||
static unsigned int yang_module_nodes_count(const struct lys_module *module);
|
||||
static void str_replace(char *o_string, const char *s_string,
|
||||
const char *r_string);
|
||||
|
||||
struct yang_mapping_node {
|
||||
char xpath_from_canonical[XPATH_MAXLEN];
|
||||
char xpath_from_fmt[XPATH_MAXLEN];
|
||||
char xpath_to_fmt[XPATH_MAXLEN];
|
||||
};
|
||||
|
||||
static bool yang_mapping_hash_cmp(const void *value1, const void *value2)
|
||||
{
|
||||
const struct yang_mapping_node *c1 = value1;
|
||||
const struct yang_mapping_node *c2 = value2;
|
||||
|
||||
return strmatch(c1->xpath_from_canonical, c2->xpath_from_canonical);
|
||||
}
|
||||
|
||||
static unsigned int yang_mapping_hash_key(void *value)
|
||||
{
|
||||
return string_hash_make(value);
|
||||
}
|
||||
|
||||
static void *yang_mapping_hash_alloc(void *p)
|
||||
{
|
||||
struct yang_mapping_node *new, *key = p;
|
||||
|
||||
new = XCALLOC(MTYPE_YANG_TRANSLATOR_MAPPING, sizeof(*new));
|
||||
strlcpy(new->xpath_from_canonical, key->xpath_from_canonical,
|
||||
sizeof(new->xpath_from_canonical));
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static void yang_mapping_hash_free(void *arg)
|
||||
{
|
||||
XFREE(MTYPE_YANG_TRANSLATOR_MAPPING, arg);
|
||||
}
|
||||
|
||||
static struct yang_mapping_node *
|
||||
yang_mapping_lookup(const struct yang_translator *translator, int dir,
|
||||
const char *xpath)
|
||||
{
|
||||
struct yang_mapping_node s;
|
||||
|
||||
strlcpy(s.xpath_from_canonical, xpath, sizeof(s.xpath_from_canonical));
|
||||
return hash_lookup(translator->mappings[dir], &s);
|
||||
}
|
||||
|
||||
static void yang_mapping_add(struct yang_translator *translator, int dir,
|
||||
const struct lys_node *snode,
|
||||
const char *xpath_from_fmt,
|
||||
const char *xpath_to_fmt)
|
||||
{
|
||||
struct yang_mapping_node *mapping, s;
|
||||
|
||||
yang_snode_get_path(snode, YANG_PATH_DATA, s.xpath_from_canonical,
|
||||
sizeof(s.xpath_from_canonical));
|
||||
mapping = hash_get(translator->mappings[dir], &s,
|
||||
yang_mapping_hash_alloc);
|
||||
strlcpy(mapping->xpath_from_fmt, xpath_from_fmt,
|
||||
sizeof(mapping->xpath_from_fmt));
|
||||
strlcpy(mapping->xpath_to_fmt, xpath_to_fmt,
|
||||
sizeof(mapping->xpath_to_fmt));
|
||||
str_replace(mapping->xpath_from_fmt, "KEY1", "%[^']");
|
||||
str_replace(mapping->xpath_from_fmt, "KEY2", "%[^']");
|
||||
str_replace(mapping->xpath_from_fmt, "KEY3", "%[^']");
|
||||
str_replace(mapping->xpath_from_fmt, "KEY4", "%[^']");
|
||||
str_replace(mapping->xpath_to_fmt, "KEY1", "%s");
|
||||
str_replace(mapping->xpath_to_fmt, "KEY2", "%s");
|
||||
str_replace(mapping->xpath_to_fmt, "KEY3", "%s");
|
||||
str_replace(mapping->xpath_to_fmt, "KEY4", "%s");
|
||||
}
|
||||
|
||||
struct yang_translator *yang_translator_load(const char *path)
|
||||
{
|
||||
struct yang_translator *translator;
|
||||
struct yang_tmodule *tmodule;
|
||||
const char *family;
|
||||
struct lyd_node *dnode;
|
||||
struct ly_set *set;
|
||||
struct listnode *ln;
|
||||
|
||||
/* Load module translator (JSON file). */
|
||||
dnode = lyd_parse_path(ly_translator_ctx, path, LYD_JSON,
|
||||
LYD_OPT_CONFIG);
|
||||
if (!dnode) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: lyd_parse_path() failed", __func__);
|
||||
return NULL;
|
||||
}
|
||||
dnode = yang_dnode_get(dnode,
|
||||
"/frr-module-translator:frr-module-translator");
|
||||
/*
|
||||
* libyang guarantees the "frr-module-translator" top-level container is
|
||||
* always present since it contains mandatory child nodes.
|
||||
*/
|
||||
assert(dnode);
|
||||
|
||||
family = yang_dnode_get_string(dnode, "./family");
|
||||
translator = yang_translator_find(family);
|
||||
if (translator != NULL) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: module translator \"%s\" is loaded already",
|
||||
__func__, family);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
translator = XCALLOC(MTYPE_YANG_TRANSLATOR, sizeof(*translator));
|
||||
strlcpy(translator->family, family, sizeof(translator->family));
|
||||
translator->modules = list_new();
|
||||
for (size_t i = 0; i < YANG_TRANSLATE_MAX; i++)
|
||||
translator->mappings[i] = hash_create(yang_mapping_hash_key,
|
||||
yang_mapping_hash_cmp,
|
||||
"YANG translation table");
|
||||
RB_INSERT(yang_translators, &yang_translators, translator);
|
||||
|
||||
/* Initialize the translator libyang context. */
|
||||
translator->ly_ctx = ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD);
|
||||
if (!translator->ly_ctx) {
|
||||
flog_warn(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
|
||||
goto error;
|
||||
}
|
||||
ly_ctx_set_searchdir(translator->ly_ctx, YANG_MODELS_PATH);
|
||||
|
||||
/* Load modules and deviations. */
|
||||
set = lyd_find_path(dnode, "./module");
|
||||
assert(set);
|
||||
for (size_t i = 0; i < set->number; i++) {
|
||||
const char *module_name;
|
||||
|
||||
tmodule =
|
||||
XCALLOC(MTYPE_YANG_TRANSLATOR_MODULE, sizeof(*tmodule));
|
||||
|
||||
module_name = yang_dnode_get_string(set->set.d[i], "./name");
|
||||
tmodule->module = ly_ctx_load_module(translator->ly_ctx,
|
||||
module_name, NULL);
|
||||
if (!tmodule->module) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: failed to load module: %s", __func__,
|
||||
module_name);
|
||||
ly_set_free(set);
|
||||
goto error;
|
||||
}
|
||||
|
||||
module_name =
|
||||
yang_dnode_get_string(set->set.d[i], "./deviations");
|
||||
tmodule->deviations = ly_ctx_load_module(translator->ly_ctx,
|
||||
module_name, NULL);
|
||||
if (!tmodule->deviations) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: failed to load module: %s", __func__,
|
||||
module_name);
|
||||
ly_set_free(set);
|
||||
goto error;
|
||||
}
|
||||
lys_set_disabled(tmodule->deviations);
|
||||
|
||||
listnode_add(translator->modules, tmodule);
|
||||
}
|
||||
ly_set_free(set);
|
||||
|
||||
/* Calculate the coverage. */
|
||||
for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
|
||||
tmodule->nodes_before_deviations =
|
||||
yang_module_nodes_count(tmodule->module);
|
||||
|
||||
lys_set_enabled(tmodule->deviations);
|
||||
|
||||
tmodule->nodes_after_deviations =
|
||||
yang_module_nodes_count(tmodule->module);
|
||||
tmodule->coverage = ((double)tmodule->nodes_after_deviations
|
||||
/ (double)tmodule->nodes_before_deviations)
|
||||
* 100;
|
||||
}
|
||||
|
||||
/* Load mappings. */
|
||||
set = lyd_find_path(dnode, "./module/mappings");
|
||||
assert(set);
|
||||
for (size_t i = 0; i < set->number; i++) {
|
||||
const char *xpath_custom, *xpath_native;
|
||||
const struct lys_node *snode_custom, *snode_native;
|
||||
|
||||
xpath_custom = yang_dnode_get_string(set->set.d[i], "./custom");
|
||||
snode_custom = ly_ctx_get_node(translator->ly_ctx, NULL,
|
||||
xpath_custom, 0);
|
||||
if (!snode_custom) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: unknown data path: %s", __func__,
|
||||
xpath_custom);
|
||||
ly_set_free(set);
|
||||
goto error;
|
||||
}
|
||||
|
||||
xpath_native = yang_dnode_get_string(set->set.d[i], "./native");
|
||||
snode_native =
|
||||
ly_ctx_get_node(ly_native_ctx, NULL, xpath_native, 0);
|
||||
if (!snode_native) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: unknown data path: %s", __func__,
|
||||
xpath_native);
|
||||
ly_set_free(set);
|
||||
goto error;
|
||||
}
|
||||
|
||||
yang_mapping_add(translator, YANG_TRANSLATE_TO_NATIVE,
|
||||
snode_custom, xpath_custom, xpath_native);
|
||||
yang_mapping_add(translator, YANG_TRANSLATE_FROM_NATIVE,
|
||||
snode_native, xpath_native, xpath_custom);
|
||||
}
|
||||
ly_set_free(set);
|
||||
|
||||
/* Validate mappings. */
|
||||
if (yang_translator_validate(translator) != 0)
|
||||
goto error;
|
||||
|
||||
yang_dnode_free(dnode);
|
||||
|
||||
return translator;
|
||||
|
||||
error:
|
||||
yang_dnode_free(dnode);
|
||||
yang_translator_unload(translator);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void yang_tmodule_delete(struct yang_tmodule *tmodule)
|
||||
{
|
||||
XFREE(MTYPE_YANG_TRANSLATOR_MODULE, tmodule);
|
||||
}
|
||||
|
||||
void yang_translator_unload(struct yang_translator *translator)
|
||||
{
|
||||
for (size_t i = 0; i < YANG_TRANSLATE_MAX; i++)
|
||||
hash_clean(translator->mappings[i], yang_mapping_hash_free);
|
||||
translator->modules->del = (void (*)(void *))yang_tmodule_delete;
|
||||
list_delete(&translator->modules);
|
||||
ly_ctx_destroy(translator->ly_ctx, NULL);
|
||||
RB_REMOVE(yang_translators, &yang_translators, translator);
|
||||
XFREE(MTYPE_YANG_TRANSLATOR, translator);
|
||||
}
|
||||
|
||||
struct yang_translator *yang_translator_find(const char *family)
|
||||
{
|
||||
struct yang_translator s;
|
||||
|
||||
strlcpy(s.family, family, sizeof(s.family));
|
||||
return RB_FIND(yang_translators, &yang_translators, &s);
|
||||
}
|
||||
|
||||
enum yang_translate_result
|
||||
yang_translate_xpath(const struct yang_translator *translator, int dir,
|
||||
char *xpath, size_t xpath_len)
|
||||
{
|
||||
struct ly_ctx *ly_ctx;
|
||||
const struct lys_node *snode;
|
||||
struct yang_mapping_node *mapping;
|
||||
char xpath_canonical[XPATH_MAXLEN];
|
||||
char keys[4][LIST_MAXKEYLEN];
|
||||
int n;
|
||||
|
||||
if (dir == YANG_TRANSLATE_TO_NATIVE)
|
||||
ly_ctx = translator->ly_ctx;
|
||||
else
|
||||
ly_ctx = ly_native_ctx;
|
||||
|
||||
snode = ly_ctx_get_node(ly_ctx, NULL, xpath, 0);
|
||||
if (!snode) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATION_ERROR,
|
||||
"%s: unknown data path: %s", __func__, xpath);
|
||||
return YANG_TRANSLATE_FAILURE;
|
||||
}
|
||||
|
||||
yang_snode_get_path(snode, YANG_PATH_DATA, xpath_canonical,
|
||||
sizeof(xpath_canonical));
|
||||
mapping = yang_mapping_lookup(translator, dir, xpath_canonical);
|
||||
if (!mapping)
|
||||
return YANG_TRANSLATE_NOTFOUND;
|
||||
|
||||
n = sscanf(xpath, mapping->xpath_from_fmt, keys[0], keys[1], keys[2],
|
||||
keys[3]);
|
||||
if (n < 0) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATION_ERROR,
|
||||
"%s: sscanf() failed: %s", __func__,
|
||||
safe_strerror(errno));
|
||||
return YANG_TRANSLATE_FAILURE;
|
||||
}
|
||||
|
||||
snprintf(xpath, xpath_len, mapping->xpath_to_fmt, keys[0], keys[1],
|
||||
keys[2], keys[3]);
|
||||
|
||||
return YANG_TRANSLATE_SUCCESS;
|
||||
}
|
||||
|
||||
int yang_translate_dnode(const struct yang_translator *translator, int dir,
|
||||
struct lyd_node **dnode)
|
||||
{
|
||||
struct ly_ctx *ly_ctx;
|
||||
struct lyd_node *new;
|
||||
struct lyd_node *root, *next, *dnode_iter;
|
||||
|
||||
/* Create new libyang data node to hold the translated data. */
|
||||
if (dir == YANG_TRANSLATE_TO_NATIVE)
|
||||
ly_ctx = ly_native_ctx;
|
||||
else
|
||||
ly_ctx = translator->ly_ctx;
|
||||
new = yang_dnode_new(ly_ctx);
|
||||
|
||||
/* Iterate over all nodes from the data tree. */
|
||||
LY_TREE_FOR (*dnode, root) {
|
||||
LY_TREE_DFS_BEGIN (root, next, dnode_iter) {
|
||||
char xpath[XPATH_MAXLEN];
|
||||
enum yang_translate_result ret;
|
||||
|
||||
yang_dnode_get_path(dnode_iter, xpath, sizeof(xpath));
|
||||
ret = yang_translate_xpath(translator, dir, xpath,
|
||||
sizeof(xpath));
|
||||
switch (ret) {
|
||||
case YANG_TRANSLATE_SUCCESS:
|
||||
break;
|
||||
case YANG_TRANSLATE_NOTFOUND:
|
||||
goto next;
|
||||
case YANG_TRANSLATE_FAILURE:
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Create new node in the tree of translated data. */
|
||||
ly_errno = 0;
|
||||
if (!lyd_new_path(new, ly_ctx, xpath,
|
||||
(void *)yang_dnode_get_string(
|
||||
dnode_iter, NULL),
|
||||
0, LYD_PATH_OPT_UPDATE)
|
||||
&& ly_errno) {
|
||||
flog_err(EC_LIB_LIBYANG,
|
||||
"%s: lyd_new_path() failed", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
next:
|
||||
LY_TREE_DFS_END(root, next, dnode_iter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace dnode by the new translated dnode. */
|
||||
yang_dnode_free(*dnode);
|
||||
*dnode = new;
|
||||
|
||||
return YANG_TRANSLATE_SUCCESS;
|
||||
|
||||
error:
|
||||
yang_dnode_free(new);
|
||||
|
||||
return YANG_TRANSLATE_FAILURE;
|
||||
}
|
||||
|
||||
static void yang_translator_validate_cb(const struct lys_node *snode_custom,
|
||||
void *arg1, void *arg2)
|
||||
{
|
||||
struct yang_translator *translator = arg1;
|
||||
unsigned int *errors = arg2;
|
||||
struct yang_mapping_node *mapping;
|
||||
const struct lys_node *snode_native;
|
||||
const struct lys_type *stype_custom, *stype_native;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
yang_snode_get_path(snode_custom, YANG_PATH_DATA, xpath, sizeof(xpath));
|
||||
mapping = yang_mapping_lookup(translator, YANG_TRANSLATE_TO_NATIVE,
|
||||
xpath);
|
||||
if (!mapping) {
|
||||
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: missing mapping for \"%s\"", __func__, xpath);
|
||||
*errors += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
snode_native =
|
||||
ly_ctx_get_node(ly_native_ctx, NULL, mapping->xpath_to_fmt, 0);
|
||||
assert(snode_native);
|
||||
|
||||
/* Check if the YANG types are compatible. */
|
||||
stype_custom = yang_snode_get_type(snode_custom);
|
||||
stype_native = yang_snode_get_type(snode_native);
|
||||
if (stype_custom && stype_native) {
|
||||
if (stype_custom->base != stype_native->base) {
|
||||
flog_warn(
|
||||
EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: YANG types are incompatible (xpath: \"%s\")",
|
||||
__func__, xpath);
|
||||
*errors += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: check if the value spaces are identical. */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the modules from the translator have a mapping for all of their
|
||||
* schema nodes (after loading the deviations).
|
||||
*/
|
||||
static unsigned int yang_translator_validate(struct yang_translator *translator)
|
||||
{
|
||||
struct yang_tmodule *tmodule;
|
||||
struct listnode *ln;
|
||||
unsigned int errors = 0;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
|
||||
yang_module_snodes_iterate(
|
||||
tmodule->module, yang_translator_validate_cb,
|
||||
YANG_ITER_FILTER_NPCONTAINERS
|
||||
| YANG_ITER_FILTER_LIST_KEYS
|
||||
| YANG_ITER_FILTER_INPUT_OUTPUT,
|
||||
translator, &errors);
|
||||
}
|
||||
|
||||
if (errors)
|
||||
flog_warn(
|
||||
EC_LIB_YANG_TRANSLATOR_LOAD,
|
||||
"%s: failed to validate \"%s\" module translator: %u error(s)",
|
||||
__func__, translator->family, errors);
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static void yang_module_nodes_count_cb(const struct lys_node *snode, void *arg1,
|
||||
void *arg2)
|
||||
{
|
||||
unsigned int *total = arg1;
|
||||
|
||||
*total += 1;
|
||||
}
|
||||
|
||||
/* Calculate the number of nodes for the given module. */
|
||||
static unsigned int yang_module_nodes_count(const struct lys_module *module)
|
||||
{
|
||||
unsigned int total = 0;
|
||||
|
||||
yang_module_snodes_iterate(module, yang_module_nodes_count_cb,
|
||||
YANG_ITER_FILTER_NPCONTAINERS
|
||||
| YANG_ITER_FILTER_LIST_KEYS
|
||||
| YANG_ITER_FILTER_INPUT_OUTPUT,
|
||||
&total, NULL);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/* TODO: rewrite this function. */
|
||||
static void str_replace(char *o_string, const char *s_string,
|
||||
const char *r_string)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
char *ch;
|
||||
|
||||
ch = strstr(o_string, s_string);
|
||||
if (!ch)
|
||||
return;
|
||||
|
||||
strncpy(buffer, o_string, ch - o_string);
|
||||
buffer[ch - o_string] = 0;
|
||||
|
||||
sprintf(buffer + (ch - o_string), "%s%s", r_string,
|
||||
ch + strlen(s_string));
|
||||
|
||||
o_string[0] = 0;
|
||||
strcpy(o_string, buffer);
|
||||
return str_replace(o_string, s_string, r_string);
|
||||
}
|
||||
|
||||
void yang_translator_init(void)
|
||||
{
|
||||
ly_translator_ctx = ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD);
|
||||
if (!ly_translator_ctx) {
|
||||
flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
|
||||
exit(1);
|
||||
}
|
||||
ly_ctx_set_searchdir(ly_translator_ctx, YANG_MODELS_PATH);
|
||||
|
||||
if (!ly_ctx_load_module(ly_translator_ctx, "frr-module-translator",
|
||||
NULL)) {
|
||||
flog_err(
|
||||
EC_LIB_YANG_MODULE_LOAD,
|
||||
"%s: failed to load the \"frr-module-translator\" module",
|
||||
__func__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void yang_translator_terminate(void)
|
||||
{
|
||||
while (!RB_EMPTY(yang_translators, &yang_translators)) {
|
||||
struct yang_translator *translator;
|
||||
|
||||
translator = RB_ROOT(yang_translators, &yang_translators);
|
||||
yang_translator_unload(translator);
|
||||
}
|
||||
|
||||
ly_ctx_destroy(ly_translator_ctx, NULL);
|
||||
}
|
144
lib/yang_translator.h
Normal file
144
lib/yang_translator.h
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _FRR_YANG_TRANSLATOR_H_
|
||||
#define _FRR_YANG_TRANSLATOR_H_
|
||||
|
||||
#define YANG_TRANSLATE_TO_NATIVE 0
|
||||
#define YANG_TRANSLATE_FROM_NATIVE 1
|
||||
#define YANG_TRANSLATE_MAX 2
|
||||
|
||||
struct yang_tmodule {
|
||||
const struct lys_module *module;
|
||||
const struct lys_module *deviations;
|
||||
uint32_t nodes_before_deviations;
|
||||
uint32_t nodes_after_deviations;
|
||||
double coverage;
|
||||
};
|
||||
|
||||
struct yang_translator {
|
||||
RB_ENTRY(yang_translator) entry;
|
||||
char family[32];
|
||||
struct ly_ctx *ly_ctx;
|
||||
struct list *modules;
|
||||
struct hash *mappings[YANG_TRANSLATE_MAX];
|
||||
};
|
||||
RB_HEAD(yang_translators, yang_translator);
|
||||
RB_PROTOTYPE(yang_translators, yang_translator, entry, yang_translator_compare);
|
||||
|
||||
enum yang_translate_result {
|
||||
YANG_TRANSLATE_SUCCESS,
|
||||
YANG_TRANSLATE_NOTFOUND,
|
||||
YANG_TRANSLATE_FAILURE,
|
||||
};
|
||||
|
||||
/* Tree of all loaded YANG module translators. */
|
||||
extern struct yang_translators yang_translators;
|
||||
|
||||
/*
|
||||
* Load a YANG module translator from a JSON file.
|
||||
*
|
||||
* path
|
||||
* Absolute path to the module translator file.
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to newly created YANG module translator, or NULL in the case of an
|
||||
* error.
|
||||
*/
|
||||
extern struct yang_translator *yang_translator_load(const char *path);
|
||||
|
||||
/*
|
||||
* Unload a YANG module translator.
|
||||
*
|
||||
* translator
|
||||
* Pointer to the YANG module translator.
|
||||
*/
|
||||
extern void yang_translator_unload(struct yang_translator *translator);
|
||||
|
||||
/*
|
||||
* Find a YANG module translator by its family name.
|
||||
*
|
||||
* family
|
||||
* Family of the YANG module translator (e.g. ietf, openconfig).
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to the YANG module translator if found, NULL otherwise.
|
||||
*/
|
||||
extern struct yang_translator *yang_translator_find(const char *family);
|
||||
|
||||
/*
|
||||
* Translate an XPath expression.
|
||||
*
|
||||
* translator
|
||||
* Pointer to YANG module translator.
|
||||
*
|
||||
* dir
|
||||
* Direction of the translation (either YANG_TRANSLATE_TO_NATIVE or
|
||||
* YANG_TRANSLATE_FROM_NATIVE).
|
||||
*
|
||||
* xpath
|
||||
* Pointer to previously allocated buffer containing the xpath expression to
|
||||
* be translated.
|
||||
*
|
||||
* xpath_len
|
||||
* Size of the xpath buffer.
|
||||
*
|
||||
* Returns:
|
||||
* - YANG_TRANSLATE_SUCCESS on success.
|
||||
* - YANG_TRANSLATE_NOTFOUND when there's no available mapping to perform
|
||||
* the translation.
|
||||
* - YANG_TRANSLATE_FAILURE when an error occurred during the translation.
|
||||
*/
|
||||
extern enum yang_translate_result
|
||||
yang_translate_xpath(const struct yang_translator *translator, int dir,
|
||||
char *xpath, size_t xpath_len);
|
||||
|
||||
/*
|
||||
* Translate an entire libyang data node.
|
||||
*
|
||||
* translator
|
||||
* Pointer to YANG module translator.
|
||||
*
|
||||
* dir
|
||||
* Direction of the translation (either YANG_TRANSLATE_TO_NATIVE or
|
||||
* YANG_TRANSLATE_FROM_NATIVE).
|
||||
*
|
||||
* dnode
|
||||
* libyang schema node we want to translate.
|
||||
*
|
||||
* Returns:
|
||||
* - YANG_TRANSLATE_SUCCESS on success.
|
||||
* - YANG_TRANSLATE_FAILURE when an error occurred during the translation.
|
||||
*/
|
||||
extern int yang_translate_dnode(const struct yang_translator *translator,
|
||||
int dir, struct lyd_node **dnode);
|
||||
|
||||
/*
|
||||
* Initialize the YANG module translator subsystem. Should be called only once
|
||||
* during the daemon initialization process.
|
||||
*/
|
||||
extern void yang_translator_init(void);
|
||||
|
||||
/*
|
||||
* Finish the YANG module translator subsystem gracefully. Should be called only
|
||||
* when the daemon is exiting.
|
||||
*/
|
||||
extern void yang_translator_terminate(void);
|
||||
|
||||
#endif /* _FRR_YANG_TRANSLATOR_H_ */
|
990
lib/yang_wrappers.c
Normal file
990
lib/yang_wrappers.c
Normal file
|
@ -0,0 +1,990 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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 "log.h"
|
||||
#include "lib_errors.h"
|
||||
#include "northbound.h"
|
||||
|
||||
static const char *yang_get_default_value(const char *xpath)
|
||||
{
|
||||
const struct lys_node *snode;
|
||||
const char *value;
|
||||
|
||||
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
|
||||
if (snode == NULL) {
|
||||
flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
||||
"%s: unknown data path: %s", __func__, xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
value = yang_snode_get_default(snode);
|
||||
assert(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#define YANG_DNODE_GET_ASSERT(dnode, xpath) \
|
||||
do { \
|
||||
if ((dnode) == NULL) { \
|
||||
flog_err(EC_LIB_YANG_DNODE_NOT_FOUND, \
|
||||
"%s: couldn't find %s", __func__, (xpath)); \
|
||||
zlog_backtrace(LOG_ERR); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Primitive type: bool.
|
||||
*/
|
||||
bool yang_str2bool(const char *value)
|
||||
{
|
||||
return strmatch(value, "true");
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_bool(const char *xpath, bool value)
|
||||
{
|
||||
return yang_data_new(xpath, (value == true) ? "true" : "false");
|
||||
}
|
||||
|
||||
bool yang_dnode_get_bool(const struct lyd_node *dnode, const char *xpath_fmt,
|
||||
...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_BOOL);
|
||||
return dleaf->value.bln;
|
||||
}
|
||||
|
||||
bool yang_get_default_bool(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2bool(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: dec64.
|
||||
*/
|
||||
double yang_str2dec64(const char *xpath, const char *value)
|
||||
{
|
||||
double dbl = 0;
|
||||
|
||||
if (sscanf(value, "%lf", &dbl) != 1) {
|
||||
flog_err(EC_LIB_YANG_DATA_CONVERT,
|
||||
"%s: couldn't convert string to decimal64 [xpath %s]",
|
||||
__func__, xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
return dbl;
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_dec64(const char *xpath, double value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%lf", value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
double yang_dnode_get_dec64(const struct lyd_node *dnode, const char *xpath_fmt,
|
||||
...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_DEC64);
|
||||
|
||||
return lyd_dec64_to_double(dnode);
|
||||
}
|
||||
|
||||
double yang_get_default_dec64(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2dec64(xpath, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: enum.
|
||||
*/
|
||||
int yang_str2enum(const char *xpath, const char *value)
|
||||
{
|
||||
const struct lys_node *snode;
|
||||
const struct lys_node_leaf *sleaf;
|
||||
const struct lys_type_info_enums *enums;
|
||||
|
||||
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
|
||||
if (snode == NULL) {
|
||||
flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
||||
"%s: unknown data path: %s", __func__, xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
sleaf = (const struct lys_node_leaf *)snode;
|
||||
enums = &sleaf->type.info.enums;
|
||||
for (unsigned int i = 0; i < enums->count; i++) {
|
||||
const struct lys_type_enum *enm = &enums->enm[i];
|
||||
|
||||
if (strmatch(value, enm->name))
|
||||
return enm->value;
|
||||
}
|
||||
|
||||
flog_err(EC_LIB_YANG_DATA_CONVERT,
|
||||
"%s: couldn't convert string to enum [xpath %s]", __func__,
|
||||
xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_enum(const char *xpath, int value)
|
||||
{
|
||||
const struct lys_node *snode;
|
||||
const struct lys_node_leaf *sleaf;
|
||||
const struct lys_type_info_enums *enums;
|
||||
|
||||
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
|
||||
if (snode == NULL) {
|
||||
flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
||||
"%s: unknown data path: %s", __func__, xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
sleaf = (const struct lys_node_leaf *)snode;
|
||||
enums = &sleaf->type.info.enums;
|
||||
for (unsigned int i = 0; i < enums->count; i++) {
|
||||
const struct lys_type_enum *enm = &enums->enm[i];
|
||||
|
||||
if (value == enm->value)
|
||||
return yang_data_new(xpath, enm->name);
|
||||
}
|
||||
|
||||
flog_err(EC_LIB_YANG_DATA_CONVERT,
|
||||
"%s: couldn't convert enum to string [xpath %s]", __func__,
|
||||
xpath);
|
||||
zlog_backtrace(LOG_ERR);
|
||||
abort();
|
||||
}
|
||||
|
||||
int yang_dnode_get_enum(const struct lyd_node *dnode, const char *xpath_fmt,
|
||||
...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_ENUM);
|
||||
return dleaf->value.enm->value;
|
||||
}
|
||||
|
||||
int yang_get_default_enum(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2enum(xpath, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: int8.
|
||||
*/
|
||||
int8_t yang_str2int8(const char *value)
|
||||
{
|
||||
return strtol(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_int8(const char *xpath, int8_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%d", value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
int8_t yang_dnode_get_int8(const struct lyd_node *dnode, const char *xpath_fmt,
|
||||
...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_INT8);
|
||||
return dleaf->value.int8;
|
||||
}
|
||||
|
||||
int8_t yang_get_default_int8(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2int8(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: int16.
|
||||
*/
|
||||
int16_t yang_str2int16(const char *value)
|
||||
{
|
||||
return strtol(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_int16(const char *xpath, int16_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%d", value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
int16_t yang_dnode_get_int16(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_INT16);
|
||||
return dleaf->value.int16;
|
||||
}
|
||||
|
||||
int16_t yang_get_default_int16(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2int16(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: int32.
|
||||
*/
|
||||
int32_t yang_str2int32(const char *value)
|
||||
{
|
||||
return strtol(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_int32(const char *xpath, int32_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%d", value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
int32_t yang_dnode_get_int32(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_INT32);
|
||||
return dleaf->value.int32;
|
||||
}
|
||||
|
||||
int32_t yang_get_default_int32(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2int32(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: int64.
|
||||
*/
|
||||
int64_t yang_str2int64(const char *value)
|
||||
{
|
||||
return strtoll(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_int64(const char *xpath, int64_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%" PRId64, value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
int64_t yang_dnode_get_int64(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_INT64);
|
||||
return dleaf->value.int64;
|
||||
}
|
||||
|
||||
int64_t yang_get_default_int64(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2int64(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: uint8.
|
||||
*/
|
||||
uint8_t yang_str2uint8(const char *value)
|
||||
{
|
||||
return strtoul(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_uint8(const char *xpath, uint8_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%u", value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
uint8_t yang_dnode_get_uint8(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_UINT8);
|
||||
return dleaf->value.uint8;
|
||||
}
|
||||
|
||||
uint8_t yang_get_default_uint8(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2uint8(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: uint16.
|
||||
*/
|
||||
uint16_t yang_str2uint16(const char *value)
|
||||
{
|
||||
return strtoul(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_uint16(const char *xpath, uint16_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%u", value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
uint16_t yang_dnode_get_uint16(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_UINT16);
|
||||
return dleaf->value.uint16;
|
||||
}
|
||||
|
||||
uint16_t yang_get_default_uint16(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2uint16(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: uint32.
|
||||
*/
|
||||
uint32_t yang_str2uint32(const char *value)
|
||||
{
|
||||
return strtoul(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_uint32(const char *xpath, uint32_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%u", value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
uint32_t yang_dnode_get_uint32(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_UINT32);
|
||||
return dleaf->value.uint32;
|
||||
}
|
||||
|
||||
uint32_t yang_get_default_uint32(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2uint32(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: uint64.
|
||||
*/
|
||||
uint64_t yang_str2uint64(const char *value)
|
||||
{
|
||||
return strtoull(value, NULL, 10);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_uint64(const char *xpath, uint64_t value)
|
||||
{
|
||||
char value_str[BUFSIZ];
|
||||
|
||||
snprintf(value_str, sizeof(value_str), "%" PRIu64, value);
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
uint64_t yang_dnode_get_uint64(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_UINT64);
|
||||
return dleaf->value.uint64;
|
||||
}
|
||||
|
||||
uint64_t yang_get_default_uint64(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
return yang_str2uint64(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primitive type: string.
|
||||
*
|
||||
* All string wrappers can be used with non-string types.
|
||||
*/
|
||||
struct yang_data *yang_data_new_string(const char *xpath, const char *value)
|
||||
{
|
||||
return yang_data_new(xpath, value);
|
||||
}
|
||||
|
||||
const char *yang_dnode_get_string(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
return dleaf->value_str;
|
||||
}
|
||||
|
||||
void yang_dnode_get_string_buf(char *buf, size_t size,
|
||||
const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
if (strlcpy(buf, dleaf->value_str, size) >= size) {
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
|
||||
flog_warn(EC_LIB_YANG_DATA_TRUNCATED,
|
||||
"%s: value was truncated [xpath %s]", __func__,
|
||||
xpath);
|
||||
}
|
||||
}
|
||||
|
||||
const char *yang_get_default_string(const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return yang_get_default_value(xpath);
|
||||
}
|
||||
|
||||
void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt,
|
||||
...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
if (strlcpy(buf, value, size) >= size)
|
||||
flog_warn(EC_LIB_YANG_DATA_TRUNCATED,
|
||||
"%s: value was truncated [xpath %s]", __func__,
|
||||
xpath);
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived type: ipv4.
|
||||
*/
|
||||
void yang_str2ipv4(const char *value, struct in_addr *addr)
|
||||
{
|
||||
(void)inet_pton(AF_INET, value, addr);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_ipv4(const char *xpath,
|
||||
const struct in_addr *addr)
|
||||
{
|
||||
char value_str[INET_ADDRSTRLEN];
|
||||
|
||||
(void)inet_ntop(AF_INET, addr, value_str, sizeof(value_str));
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
void yang_dnode_get_ipv4(struct in_addr *addr, const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_STRING);
|
||||
memcpy(addr, dleaf->value.ptr, sizeof(*addr));
|
||||
}
|
||||
|
||||
void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
yang_str2ipv4(value, var);
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived type: ipv4p.
|
||||
*/
|
||||
void yang_str2ipv4p(const char *value, union prefixptr prefix)
|
||||
{
|
||||
struct prefix_ipv4 *prefix4 = prefix.p4;
|
||||
|
||||
(void)str2prefix_ipv4(value, prefix4);
|
||||
apply_mask_ipv4(prefix4);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_ipv4p(const char *xpath,
|
||||
const union prefixptr prefix)
|
||||
{
|
||||
char value_str[PREFIX2STR_BUFFER];
|
||||
|
||||
(void)prefix2str(prefix.p, value_str, sizeof(value_str));
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
void yang_dnode_get_ipv4p(union prefixptr prefix, const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
struct prefix_ipv4 *prefix4 = prefix.p4;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_STRING);
|
||||
memcpy(prefix4, dleaf->value.ptr, sizeof(*prefix4));
|
||||
}
|
||||
|
||||
void yang_get_default_ipv4p(union prefixptr var, const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
yang_str2ipv4p(value, var);
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived type: ipv6.
|
||||
*/
|
||||
void yang_str2ipv6(const char *value, struct in6_addr *addr)
|
||||
{
|
||||
(void)inet_pton(AF_INET6, value, addr);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_ipv6(const char *xpath,
|
||||
const struct in6_addr *addr)
|
||||
{
|
||||
char value_str[INET6_ADDRSTRLEN];
|
||||
|
||||
(void)inet_ntop(AF_INET6, addr, value_str, sizeof(value_str));
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
void yang_dnode_get_ipv6(struct in6_addr *addr, const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_STRING);
|
||||
memcpy(addr, dleaf->value.ptr, sizeof(*addr));
|
||||
}
|
||||
|
||||
void yang_get_default_ipv6(struct in6_addr *var, const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
yang_str2ipv6(value, var);
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived type: ipv6p.
|
||||
*/
|
||||
void yang_str2ipv6p(const char *value, union prefixptr prefix)
|
||||
{
|
||||
struct prefix_ipv6 *prefix6 = prefix.p6;
|
||||
|
||||
(void)str2prefix_ipv6(value, prefix6);
|
||||
apply_mask_ipv6(prefix6);
|
||||
}
|
||||
|
||||
struct yang_data *yang_data_new_ipv6p(const char *xpath,
|
||||
const union prefixptr prefix)
|
||||
{
|
||||
char value_str[PREFIX2STR_BUFFER];
|
||||
|
||||
(void)prefix2str(prefix.p, value_str, sizeof(value_str));
|
||||
return yang_data_new(xpath, value_str);
|
||||
}
|
||||
|
||||
void yang_dnode_get_ipv6p(union prefixptr prefix, const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...)
|
||||
{
|
||||
const struct lyd_node_leaf_list *dleaf;
|
||||
struct prefix_ipv6 *prefix6 = prefix.p6;
|
||||
|
||||
assert(dnode);
|
||||
if (xpath_fmt) {
|
||||
va_list ap;
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
dnode = yang_dnode_get(dnode, xpath);
|
||||
YANG_DNODE_GET_ASSERT(dnode, xpath);
|
||||
}
|
||||
|
||||
dleaf = (const struct lyd_node_leaf_list *)dnode;
|
||||
assert(dleaf->value_type == LY_TYPE_STRING);
|
||||
memcpy(prefix6, dleaf->value.ptr, sizeof(*prefix6));
|
||||
}
|
||||
|
||||
void yang_get_default_ipv6p(union prefixptr var, const char *xpath_fmt, ...)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
const char *value;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, xpath_fmt);
|
||||
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
value = yang_get_default_value(xpath);
|
||||
yang_str2ipv6p(value, var);
|
||||
}
|
157
lib/yang_wrappers.h
Normal file
157
lib/yang_wrappers.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _FRR_NORTHBOUND_WRAPPERS_H_
|
||||
#define _FRR_NORTHBOUND_WRAPPERS_H_
|
||||
|
||||
#include "prefix.h"
|
||||
|
||||
/* bool */
|
||||
extern bool yang_str2bool(const char *value);
|
||||
extern struct yang_data *yang_data_new_bool(const char *xpath, bool value);
|
||||
extern bool yang_dnode_get_bool(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern bool yang_get_default_bool(const char *xpath_fmt, ...);
|
||||
|
||||
/* dec64 */
|
||||
extern double yang_str2dec64(const char *xpath, const char *value);
|
||||
extern struct yang_data *yang_data_new_dec64(const char *xpath, double value);
|
||||
extern double yang_dnode_get_dec64(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern double yang_get_default_dec64(const char *xpath_fmt, ...);
|
||||
|
||||
/* enum */
|
||||
extern int yang_str2enum(const char *xpath, const char *value);
|
||||
extern struct yang_data *yang_data_new_enum(const char *xpath, int value);
|
||||
extern int yang_dnode_get_enum(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern int yang_get_default_enum(const char *xpath_fmt, ...);
|
||||
|
||||
/* int8 */
|
||||
extern int8_t yang_str2int8(const char *value);
|
||||
extern struct yang_data *yang_data_new_int8(const char *xpath, int8_t value);
|
||||
extern int8_t yang_dnode_get_int8(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern int8_t yang_get_default_int8(const char *xpath_fmt, ...);
|
||||
|
||||
/* int16 */
|
||||
extern int16_t yang_str2int16(const char *value);
|
||||
extern struct yang_data *yang_data_new_int16(const char *xpath, int16_t value);
|
||||
extern int16_t yang_dnode_get_int16(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern int16_t yang_get_default_int16(const char *xpath_fmt, ...);
|
||||
|
||||
/* int32 */
|
||||
extern int32_t yang_str2int32(const char *value);
|
||||
extern struct yang_data *yang_data_new_int32(const char *xpath, int32_t value);
|
||||
extern int32_t yang_dnode_get_int32(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern int32_t yang_get_default_int32(const char *xpath_fmt, ...);
|
||||
|
||||
/* int64 */
|
||||
extern int64_t yang_str2int64(const char *value);
|
||||
extern struct yang_data *yang_data_new_int64(const char *xpath, int64_t value);
|
||||
extern int64_t yang_dnode_get_int64(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern int64_t yang_get_default_int64(const char *xpath_fmt, ...);
|
||||
|
||||
/* uint8 */
|
||||
extern uint8_t yang_str2uint8(const char *value);
|
||||
extern struct yang_data *yang_data_new_uint8(const char *xpath, uint8_t value);
|
||||
extern uint8_t yang_dnode_get_uint8(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern uint8_t yang_get_default_uint8(const char *xpath_fmt, ...);
|
||||
|
||||
/* uint16 */
|
||||
extern uint16_t yang_str2uint16(const char *value);
|
||||
extern struct yang_data *yang_data_new_uint16(const char *xpath,
|
||||
uint16_t value);
|
||||
extern uint16_t yang_dnode_get_uint16(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern uint16_t yang_get_default_uint16(const char *xpath_fmt, ...);
|
||||
|
||||
/* uint32 */
|
||||
extern uint32_t yang_str2uint32(const char *value);
|
||||
extern struct yang_data *yang_data_new_uint32(const char *xpath,
|
||||
uint32_t value);
|
||||
extern uint32_t yang_dnode_get_uint32(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern uint32_t yang_get_default_uint32(const char *xpath_fmt, ...);
|
||||
|
||||
/* uint64 */
|
||||
extern uint64_t yang_str2uint64(const char *value);
|
||||
extern struct yang_data *yang_data_new_uint64(const char *xpath,
|
||||
uint64_t value);
|
||||
extern uint64_t yang_dnode_get_uint64(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern uint64_t yang_get_default_uint64(const char *xpath_fmt, ...);
|
||||
|
||||
/* string */
|
||||
extern struct yang_data *yang_data_new_string(const char *xpath,
|
||||
const char *value);
|
||||
extern const char *yang_dnode_get_string(const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern void yang_dnode_get_string_buf(char *buf, size_t size,
|
||||
const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern const char *yang_get_default_string(const char *xpath_fmt, ...);
|
||||
extern void yang_get_default_string_buf(char *buf, size_t size,
|
||||
const char *xpath_fmt, ...);
|
||||
|
||||
/* ipv4 */
|
||||
extern void yang_str2ipv4(const char *value, struct in_addr *addr);
|
||||
extern struct yang_data *yang_data_new_ipv4(const char *xpath,
|
||||
const struct in_addr *addr);
|
||||
extern void yang_dnode_get_ipv4(struct in_addr *addr,
|
||||
const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt,
|
||||
...);
|
||||
|
||||
/* ipv4p */
|
||||
extern void yang_str2ipv4p(const char *value, union prefixptr prefix);
|
||||
extern struct yang_data *yang_data_new_ipv4p(const char *xpath,
|
||||
const union prefixptr prefix);
|
||||
extern void yang_dnode_get_ipv4p(union prefixptr prefix,
|
||||
const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern void yang_get_default_ipv4p(union prefixptr var, const char *xpath_fmt,
|
||||
...);
|
||||
|
||||
/* ipv6 */
|
||||
extern void yang_str2ipv6(const char *value, struct in6_addr *addr);
|
||||
extern struct yang_data *yang_data_new_ipv6(const char *xpath,
|
||||
const struct in6_addr *addr);
|
||||
extern void yang_dnode_get_ipv6(struct in6_addr *addr,
|
||||
const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern void yang_get_default_ipv6(struct in6_addr *var, const char *xpath_fmt,
|
||||
...);
|
||||
|
||||
/* ipv6p */
|
||||
extern void yang_str2ipv6p(const char *value, union prefixptr prefix);
|
||||
extern struct yang_data *yang_data_new_ipv6p(const char *xpath,
|
||||
const union prefixptr prefix);
|
||||
extern void yang_dnode_get_ipv6p(union prefixptr prefix,
|
||||
const struct lyd_node *dnode,
|
||||
const char *xpath_fmt, ...);
|
||||
extern void yang_get_default_ipv6p(union prefixptr var, const char *xpath_fmt,
|
||||
...);
|
||||
|
||||
#endif /* _FRR_NORTHBOUND_WRAPPERS_H_ */
|
|
@ -76,7 +76,7 @@ static void sighup(void)
|
|||
zlog_info("ripd restarting!");
|
||||
|
||||
/* Reload config file. */
|
||||
vty_read_config(ripd_di.config_file, config_default);
|
||||
vty_read_config(NULL, ripd_di.config_file, config_default);
|
||||
|
||||
/* Try to return to normal operation. */
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ static void sighup(void)
|
|||
ripng_reset();
|
||||
|
||||
/* Reload config file. */
|
||||
vty_read_config(ripngd_di.config_file, config_default);
|
||||
vty_read_config(NULL, ripngd_di.config_file, config_default);
|
||||
|
||||
/* Try to return to normal operation. */
|
||||
}
|
||||
|
|
|
@ -1383,6 +1383,8 @@ static void bgp_startup(void)
|
|||
LOG_DAEMON);
|
||||
zprivs_preinit(&bgpd_privs);
|
||||
zprivs_init(&bgpd_privs);
|
||||
yang_init();
|
||||
nb_init(NULL, 0);
|
||||
|
||||
master = thread_master_create(NULL);
|
||||
bgp_master_init(master);
|
||||
|
@ -1428,6 +1430,8 @@ static void bgp_shutdown(void)
|
|||
|
||||
vty_terminate();
|
||||
cmd_terminate();
|
||||
nb_terminate();
|
||||
yang_terminate();
|
||||
zprivs_terminate(&bgpd_privs);
|
||||
thread_master_free(master);
|
||||
master = NULL;
|
||||
|
|
|
@ -155,6 +155,8 @@ int main(int argc, char **argv)
|
|||
cmd_init(1);
|
||||
vty_init(master);
|
||||
memory_init();
|
||||
yang_init();
|
||||
nb_init(NULL, 0);
|
||||
|
||||
/* OSPF vty inits. */
|
||||
test_vty_init();
|
||||
|
@ -171,7 +173,7 @@ int main(int argc, char **argv)
|
|||
/* Configuration file read*/
|
||||
if (!config_file)
|
||||
usage(progname, 1);
|
||||
vty_read_config(config_file, NULL);
|
||||
vty_read_config(NULL, config_file, NULL);
|
||||
|
||||
test_timer_init();
|
||||
|
||||
|
|
|
@ -50,6 +50,8 @@ static void vty_do_exit(int isexit)
|
|||
printf("\nend.\n");
|
||||
cmd_terminate();
|
||||
vty_terminate();
|
||||
nb_terminate();
|
||||
yang_terminate();
|
||||
thread_master_free(master);
|
||||
closezlog();
|
||||
|
||||
|
@ -81,6 +83,8 @@ int main(int argc, char **argv)
|
|||
|
||||
vty_init(master);
|
||||
memory_init();
|
||||
yang_init();
|
||||
nb_init(NULL, 0);
|
||||
|
||||
test_init(argc, argv);
|
||||
|
||||
|
|
|
@ -313,6 +313,7 @@ frr defaults @DFLT_NAME@
|
|||
hostname test
|
||||
!
|
||||
!
|
||||
!
|
||||
line vty
|
||||
!
|
||||
end
|
||||
|
@ -328,6 +329,7 @@ frr defaults @DFLT_NAME@
|
|||
hostname foohost
|
||||
!
|
||||
!
|
||||
!
|
||||
line vty
|
||||
!
|
||||
end
|
||||
|
|
|
@ -142,6 +142,8 @@ static void test_init(void)
|
|||
struct cmd_element *cmd;
|
||||
|
||||
cmd_init(1);
|
||||
yang_init();
|
||||
nb_init(NULL, 0);
|
||||
|
||||
install_node(&bgp_node, NULL);
|
||||
install_node(&rip_node, NULL);
|
||||
|
@ -184,6 +186,8 @@ static void test_terminate(void)
|
|||
XFREE(MTYPE_TMP, vector_slot(test_cmds, i));
|
||||
vector_free(test_cmds);
|
||||
cmd_terminate();
|
||||
nb_terminate();
|
||||
yang_terminate();
|
||||
}
|
||||
|
||||
static void test_run(struct prng *prng, struct vty *vty, const char *cmd,
|
||||
|
|
2
tools/.gitignore
vendored
2
tools/.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
/frr
|
||||
/gen_northbound_callbacks
|
||||
/gen_yang_deviations
|
||||
/permutations
|
||||
/ssd
|
||||
|
|
302
tools/gen_northbound_callbacks.c
Normal file
302
tools/gen_northbound_callbacks.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define REALLY_NEED_PLAIN_GETOPT 1
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "yang.h"
|
||||
#include "northbound.h"
|
||||
|
||||
static void __attribute__((noreturn)) usage(int status)
|
||||
{
|
||||
fprintf(stderr, "usage: gen_northbound_callbacks [-h] MODULE\n");
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static struct nb_callback_info {
|
||||
int operation;
|
||||
bool optional;
|
||||
char return_type[32];
|
||||
char return_value[32];
|
||||
char arguments[128];
|
||||
} nb_callbacks[] = {
|
||||
{
|
||||
.operation = NB_OP_CREATE,
|
||||
.return_type = "int ",
|
||||
.return_value = "NB_OK",
|
||||
.arguments =
|
||||
"enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_MODIFY,
|
||||
.return_type = "int ",
|
||||
.return_value = "NB_OK",
|
||||
.arguments =
|
||||
"enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_DELETE,
|
||||
.return_type = "int ",
|
||||
.return_value = "NB_OK",
|
||||
.arguments =
|
||||
"enum nb_event event, const struct lyd_node *dnode",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_MOVE,
|
||||
.return_type = "int ",
|
||||
.return_value = "NB_OK",
|
||||
.arguments =
|
||||
"enum nb_event event, const struct lyd_node *dnode",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_APPLY_FINISH,
|
||||
.optional = true,
|
||||
.return_type = "void ",
|
||||
.return_value = "",
|
||||
.arguments = "const struct lyd_node *dnode",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_GET_ELEM,
|
||||
.return_type = "struct yang_data *",
|
||||
.return_value = "NULL",
|
||||
.arguments = "const char *xpath, const void *list_entry",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_GET_NEXT,
|
||||
.return_type = "const void *",
|
||||
.return_value = "NULL",
|
||||
.arguments = "const char *xpath, const void *list_entry",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_GET_KEYS,
|
||||
.return_type = "int ",
|
||||
.return_value = "NB_OK",
|
||||
.arguments = "const void *list_entry, struct yang_list_keys *keys",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_LOOKUP_ENTRY,
|
||||
.return_type = "const void *",
|
||||
.return_value = "NULL",
|
||||
.arguments = "const struct yang_list_keys *keys",
|
||||
},
|
||||
{
|
||||
.operation = NB_OP_RPC,
|
||||
.return_type = "int ",
|
||||
.return_value = "NB_OK",
|
||||
.arguments =
|
||||
"const char *xpath, const struct list *input, struct list *output",
|
||||
},
|
||||
{
|
||||
/* sentinel */
|
||||
.operation = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static void replace_hyphens_by_underscores(char *str)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = str;
|
||||
while ((p = strchr(p, '-')) != NULL)
|
||||
*p++ = '_';
|
||||
}
|
||||
|
||||
static void generate_callback_name(struct lys_node *snode,
|
||||
enum nb_operation operation, char *buffer,
|
||||
size_t size)
|
||||
{
|
||||
struct list *snodes;
|
||||
struct listnode *ln;
|
||||
|
||||
snodes = list_new();
|
||||
for (; snode; snode = lys_parent(snode)) {
|
||||
/* Skip schema-only snodes. */
|
||||
if (snode->nodetype
|
||||
& (LYS_USES | LYS_CHOICE | LYS_CASE | LYS_INPUT
|
||||
| LYS_OUTPUT))
|
||||
continue;
|
||||
|
||||
listnode_add_head(snodes, snode);
|
||||
}
|
||||
|
||||
memset(buffer, 0, size);
|
||||
for (ALL_LIST_ELEMENTS_RO(snodes, ln, snode)) {
|
||||
strlcat(buffer, snode->name, size);
|
||||
strlcat(buffer, "_", size);
|
||||
}
|
||||
strlcat(buffer, nb_operation_name(operation), size);
|
||||
list_delete(&snodes);
|
||||
|
||||
replace_hyphens_by_underscores(buffer);
|
||||
}
|
||||
|
||||
static void generate_callbacks(const struct lys_node *snode, void *arg1,
|
||||
void *arg2)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
switch (snode->nodetype) {
|
||||
case LYS_CONTAINER:
|
||||
case LYS_LEAF:
|
||||
case LYS_LEAFLIST:
|
||||
case LYS_LIST:
|
||||
case LYS_NOTIF:
|
||||
case LYS_RPC:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
for (struct nb_callback_info *cb = &nb_callbacks[0];
|
||||
cb->operation != -1; cb++) {
|
||||
char cb_name[BUFSIZ];
|
||||
|
||||
if (cb->optional
|
||||
|| !nb_operation_is_valid(cb->operation, snode))
|
||||
continue;
|
||||
|
||||
if (first) {
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
yang_snode_get_path(snode, YANG_PATH_DATA, xpath,
|
||||
sizeof(xpath));
|
||||
|
||||
printf("/*\n"
|
||||
" * XPath: %s\n"
|
||||
" */\n",
|
||||
xpath);
|
||||
first = false;
|
||||
}
|
||||
|
||||
generate_callback_name((struct lys_node *)snode, cb->operation,
|
||||
cb_name, sizeof(cb_name));
|
||||
printf("static %s%s(%s)\n"
|
||||
"{\n"
|
||||
"\t/* TODO: implement me. */\n"
|
||||
"\treturn %s;\n"
|
||||
"}\n\n",
|
||||
nb_callbacks[cb->operation].return_type, cb_name,
|
||||
nb_callbacks[cb->operation].arguments,
|
||||
nb_callbacks[cb->operation].return_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_nb_nodes(const struct lys_node *snode, void *arg1,
|
||||
void *arg2)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
switch (snode->nodetype) {
|
||||
case LYS_CONTAINER:
|
||||
case LYS_LEAF:
|
||||
case LYS_LEAFLIST:
|
||||
case LYS_LIST:
|
||||
case LYS_NOTIF:
|
||||
case LYS_RPC:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
for (struct nb_callback_info *cb = &nb_callbacks[0];
|
||||
cb->operation != -1; cb++) {
|
||||
char cb_name[BUFSIZ];
|
||||
|
||||
if (cb->optional
|
||||
|| !nb_operation_is_valid(cb->operation, snode))
|
||||
continue;
|
||||
|
||||
if (first) {
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
yang_snode_get_path(snode, YANG_PATH_DATA, xpath,
|
||||
sizeof(xpath));
|
||||
|
||||
printf("\t\t{\n"
|
||||
"\t\t\t.xpath = \"%s\",\n",
|
||||
xpath);
|
||||
first = false;
|
||||
}
|
||||
|
||||
generate_callback_name((struct lys_node *)snode, cb->operation,
|
||||
cb_name, sizeof(cb_name));
|
||||
printf("\t\t\t.cbs.%s = %s,\n",
|
||||
nb_operation_name(cb->operation), cb_name);
|
||||
}
|
||||
|
||||
if (!first)
|
||||
printf("\t\t},\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct yang_module *module;
|
||||
char module_name_underscores[64];
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(EXIT_SUCCESS);
|
||||
/* NOTREACHED */
|
||||
default:
|
||||
usage(EXIT_FAILURE);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc != 1)
|
||||
usage(EXIT_FAILURE);
|
||||
|
||||
yang_init();
|
||||
|
||||
/* Load YANG module. */
|
||||
module = yang_module_load(argv[0]);
|
||||
|
||||
/* Generate callback functions. */
|
||||
yang_module_snodes_iterate(module->info, generate_callbacks, 0, NULL,
|
||||
NULL);
|
||||
|
||||
strlcpy(module_name_underscores, module->name,
|
||||
sizeof(module_name_underscores));
|
||||
replace_hyphens_by_underscores(module_name_underscores);
|
||||
|
||||
/* Generate frr_yang_module_info array. */
|
||||
printf("/* clang-format off */\n"
|
||||
"const struct frr_yang_module_info %s_info = {\n"
|
||||
"\t.name = \"%s\",\n"
|
||||
"\t.nodes = {\n",
|
||||
module_name_underscores, module->name);
|
||||
yang_module_snodes_iterate(module->info, generate_nb_nodes, 0, NULL,
|
||||
NULL);
|
||||
printf("\t\t{\n"
|
||||
"\t\t\t.xpath = NULL,\n"
|
||||
"\t\t},\n");
|
||||
printf("\t}\n"
|
||||
"};\n");
|
||||
|
||||
/* Cleanup and exit. */
|
||||
yang_terminate();
|
||||
|
||||
return 0;
|
||||
}
|
80
tools/gen_yang_deviations.c
Normal file
80
tools/gen_yang_deviations.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define REALLY_NEED_PLAIN_GETOPT 1
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "yang.h"
|
||||
#include "northbound.h"
|
||||
|
||||
static void __attribute__((noreturn)) usage(int status)
|
||||
{
|
||||
fprintf(stderr, "usage: gen_yang_deviations [-h] MODULE\n");
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static void generate_yang_deviation(const struct lys_node *snode, void *arg1,
|
||||
void *arg2)
|
||||
{
|
||||
char xpath[XPATH_MAXLEN];
|
||||
|
||||
yang_snode_get_path(snode, YANG_PATH_SCHEMA, xpath, sizeof(xpath));
|
||||
|
||||
printf(" deviation \"%s\" {\n", xpath);
|
||||
printf(" deviate not-supported;\n");
|
||||
printf(" }\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct yang_module *module;
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(EXIT_SUCCESS);
|
||||
/* NOTREACHED */
|
||||
default:
|
||||
usage(EXIT_FAILURE);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc != 1)
|
||||
usage(EXIT_FAILURE);
|
||||
|
||||
yang_init();
|
||||
|
||||
/* Load YANG module. */
|
||||
module = yang_module_load(argv[0]);
|
||||
|
||||
/* Generate deviations. */
|
||||
yang_module_snodes_iterate(module->info, generate_yang_deviation,
|
||||
YANG_ITER_FILTER_IMPLICIT, NULL, NULL);
|
||||
|
||||
/* Cleanup and exit. */
|
||||
yang_terminate();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -2,7 +2,12 @@
|
|||
# tools
|
||||
#
|
||||
|
||||
noinst_PROGRAMS += tools/permutations
|
||||
noinst_PROGRAMS += \
|
||||
tools/permutations \
|
||||
tools/gen_northbound_callbacks \
|
||||
tools/gen_yang_deviations \
|
||||
# end
|
||||
|
||||
sbin_PROGRAMS += tools/ssd
|
||||
sbin_SCRIPTS += \
|
||||
tools/frr-reload \
|
||||
|
@ -13,6 +18,12 @@ sbin_SCRIPTS += \
|
|||
tools_permutations_SOURCES = tools/permutations.c
|
||||
tools_permutations_LDADD = lib/libfrr.la
|
||||
|
||||
tools_gen_northbound_callbacks_SOURCES = tools/gen_northbound_callbacks.c
|
||||
tools_gen_northbound_callbacks_LDADD = lib/libfrr.la -lyang
|
||||
|
||||
tools_gen_yang_deviations_SOURCES = tools/gen_yang_deviations.c
|
||||
tools_gen_yang_deviations_LDADD = lib/libfrr.la -lyang
|
||||
|
||||
tools_ssd_SOURCES = tools/start-stop-daemon.c
|
||||
|
||||
EXTRA_DIST += \
|
||||
|
|
|
@ -366,6 +366,10 @@ void vtysh_config_parse_line(void *arg, const char *line)
|
|||
config = config_get(FORWARDING_NODE, line);
|
||||
else if (strncmp(line, "debug vrf", strlen("debug vrf")) == 0)
|
||||
config = config_get(VRF_DEBUG_NODE, line);
|
||||
else if (strncmp(line, "debug northbound",
|
||||
strlen("debug northbound"))
|
||||
== 0)
|
||||
config = config_get(NORTHBOUND_DEBUG_NODE, line);
|
||||
else if (strncmp(line, "debug", strlen("debug")) == 0)
|
||||
config = config_get(DEBUG_NODE, line);
|
||||
else if (strncmp(line, "password", strlen("password")) == 0
|
||||
|
@ -411,7 +415,7 @@ void vtysh_config_parse_line(void *arg, const char *line)
|
|||
|| (I) == ACCESS_IPV6_NODE || (I) == ACCESS_MAC_NODE \
|
||||
|| (I) == PREFIX_IPV6_NODE || (I) == FORWARDING_NODE \
|
||||
|| (I) == DEBUG_NODE || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE \
|
||||
|| (I) == MPLS_NODE)
|
||||
|| (I) == NORTHBOUND_DEBUG_NODE || (I) == MPLS_NODE)
|
||||
|
||||
/* Display configuration to file pointer. */
|
||||
void vtysh_config_dump(void)
|
||||
|
|
68
yang/frr-module-translator.yang
Normal file
68
yang/frr-module-translator.yang
Normal file
|
@ -0,0 +1,68 @@
|
|||
module frr-module-translator {
|
||||
yang-version 1.1;
|
||||
namespace "http://frrouting.org/yang/frr-module-translator";
|
||||
prefix frr-module-translator;
|
||||
|
||||
organization
|
||||
"Free Range Routing";
|
||||
contact
|
||||
"FRR Users List: <mailto:frog@lists.frrouting.org>
|
||||
FRR Development List: <mailto:dev@lists.frrouting.org>";
|
||||
description
|
||||
"A model for FRR YANG module translators.";
|
||||
|
||||
revision 2018-07-31 {
|
||||
description
|
||||
"Initial revision.";
|
||||
}
|
||||
|
||||
container frr-module-translator {
|
||||
leaf family {
|
||||
type string {
|
||||
length "0 .. 32";
|
||||
}
|
||||
mandatory true;
|
||||
description
|
||||
"Family of YANG models.";
|
||||
}
|
||||
list module {
|
||||
key "name";
|
||||
ordered-by user;
|
||||
description
|
||||
"YANG module.";
|
||||
|
||||
leaf name {
|
||||
type string;
|
||||
description
|
||||
"Module name.";
|
||||
}
|
||||
leaf deviations {
|
||||
type string;
|
||||
mandatory true;
|
||||
description
|
||||
"Module containing the YANG deviations.";
|
||||
}
|
||||
list mappings {
|
||||
key "custom";
|
||||
description
|
||||
"YANG mappings between the custom module and FRR native modules.";
|
||||
|
||||
leaf custom {
|
||||
type string {
|
||||
length "0 .. 256";
|
||||
}
|
||||
description
|
||||
"YANG path of the custom module.";
|
||||
}
|
||||
leaf native {
|
||||
type string {
|
||||
length "0 .. 256";
|
||||
}
|
||||
mandatory true;
|
||||
description
|
||||
"Corresponding path of the native YANG modules";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
yang/libyang_plugins/frr_user_types.c
Normal file
100
yang/libyang_plugins/frr_user_types.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (C) 2018 NetDEF, Inc.
|
||||
* Renato Westphal
|
||||
*
|
||||
* 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 "prefix.h"
|
||||
|
||||
#include <libyang/user_types.h>
|
||||
|
||||
static int ipv4_address_store_clb(const char *type_name, const char *value_str,
|
||||
lyd_val *value, char **err_msg)
|
||||
{
|
||||
value->ptr = malloc(sizeof(struct in_addr));
|
||||
if (!value->ptr)
|
||||
return 1;
|
||||
|
||||
if (inet_pton(AF_INET, value_str, value->ptr) != 1) {
|
||||
free(value->ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipv6_address_store_clb(const char *type_name, const char *value_str,
|
||||
lyd_val *value, char **err_msg)
|
||||
{
|
||||
value->ptr = malloc(INET6_ADDRSTRLEN);
|
||||
if (!value->ptr)
|
||||
return 1;
|
||||
|
||||
if (inet_pton(AF_INET6, value_str, value->ptr) != 1) {
|
||||
free(value->ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipv4_prefix_store_clb(const char *type_name, const char *value_str,
|
||||
lyd_val *value, char **err_msg)
|
||||
{
|
||||
value->ptr = malloc(sizeof(struct prefix_ipv4));
|
||||
if (!value->ptr)
|
||||
return 1;
|
||||
|
||||
if (str2prefix_ipv4(value_str, value->ptr) == 0) {
|
||||
free(value->ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipv6_prefix_store_clb(const char *type_name, const char *value_str,
|
||||
lyd_val *value, char **err_msg)
|
||||
{
|
||||
value->ptr = malloc(sizeof(struct prefix_ipv6));
|
||||
if (!value->ptr)
|
||||
return 1;
|
||||
|
||||
if (str2prefix_ipv6(value_str, value->ptr) == 0) {
|
||||
free(value->ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lytype_plugin_list frr_user_types[] = {
|
||||
{"ietf-inet-types", "2013-07-15", "ipv4-address",
|
||||
ipv4_address_store_clb, free},
|
||||
{"ietf-inet-types", "2013-07-15", "ipv4-address-no-zone",
|
||||
ipv4_address_store_clb, free},
|
||||
{"ietf-inet-types", "2013-07-15", "ipv6-address",
|
||||
ipv6_address_store_clb, free},
|
||||
{"ietf-inet-types", "2013-07-15", "ipv6-address-no-zone",
|
||||
ipv6_address_store_clb, free},
|
||||
{"ietf-inet-types", "2013-07-15", "ipv4-prefix", ipv4_prefix_store_clb,
|
||||
free},
|
||||
{"ietf-inet-types", "2013-07-15", "ipv6-prefix", ipv6_prefix_store_clb,
|
||||
free},
|
||||
{NULL, NULL, NULL, NULL, NULL} /* terminating item */
|
||||
};
|
9
yang/libyang_plugins/subdir.am
Normal file
9
yang/libyang_plugins/subdir.am
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# libyang user types
|
||||
#
|
||||
libyang_plugins_LTLIBRARIES += yang/libyang_plugins/frr_user_types.la
|
||||
|
||||
yang_libyang_plugins_frr_user_types_la_CFLAGS = $(WERROR)
|
||||
yang_libyang_plugins_frr_user_types_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
|
||||
yang_libyang_plugins_frr_user_types_la_LIBADD =
|
||||
yang_libyang_plugins_frr_user_types_la_SOURCES = yang/libyang_plugins/frr_user_types.c
|
1
yang/subdir.am
Normal file
1
yang/subdir.am
Normal file
|
@ -0,0 +1 @@
|
|||
dist_yangmodels_DATA += yang/frr-module-translator.yang
|
Loading…
Reference in a new issue