1
0
Fork 0

Adding upstream version 3.1.0+dfsg.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 08:00:08 +01:00
parent 64dbec996d
commit cfcebb1a7d
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
569 changed files with 205393 additions and 0 deletions

25
tools/CMakeLists.txt Normal file
View file

@ -0,0 +1,25 @@
include(CheckIncludeFile)
# config file for tools
configure_file(${PROJECT_SOURCE_DIR}/tools/config.h.in ${PROJECT_BINARY_DIR}/tools/config.h @ONLY)
# find ioctl
check_include_file("sys/ioctl.h" HAVE_IOCTL)
if(NOT HAVE_IOCTL)
message(STATUS "Disabling interactive yanglint, sys/ioctl.h not found...")
set(ENABLE_YANGLINT_INTERACTIVE OFF)
endif()
# find getopt library on WIN32
if(WIN32)
find_library(GETOPT_LIBRARY NAMES getopt REQUIRED)
find_path(GETOPT_INCLUDE_DIR NAMES getopt.h REQUIRED)
message(STATUS "Found <getopt.h> at ${GETOPT_INCLUDE_DIR}, library at ${GETOPT_LIBRARY}")
endif()
add_subdirectory(lint)
add_subdirectory(re)
set(format_sources
${format_sources}
PARENT_SCOPE)

20
tools/config.h.in Normal file
View file

@ -0,0 +1,20 @@
/**
* @file config.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief various variables detected by cmake
*
* Copyright (c) 2019 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef YANGLINT_CONFIG_H_
#define YANGLINT_CONFIG_H_
#define PROJECT_VERSION "@LIBYANG_VERSION@" /**< libyang project version string */
#endif /* YANGLINT_CONFIG_H_ */

48
tools/lint/CMakeLists.txt Normal file
View file

@ -0,0 +1,48 @@
# yanglint
set(lintsrc
main_ni.c
cmd.c
cmd_add.c
cmd_clear.c
cmd_data.c
cmd_list.c
cmd_feature.c
cmd_load.c
cmd_print.c
cmd_searchpath.c
cmd_extdata.c
cmd_help.c
cmd_verb.c
cmd_debug.c
yl_opt.c
yl_schema_features.c
common.c
)
if(ENABLE_YANGLINT_INTERACTIVE)
set(lintsrc ${lintsrc}
main.c
completion.c
configuration.c
linenoise/linenoise.c)
else()
set(lintsrc ${lintsrc}
main_ni_only.c)
endif()
set(format_sources
${format_sources}
${CMAKE_CURRENT_SOURCE_DIR}/*.c
${CMAKE_CURRENT_SOURCE_DIR}/*.h
PARENT_SCOPE)
add_executable(yanglint ${lintsrc} ${compatsrc})
target_link_libraries(yanglint yang)
install(TARGETS yanglint DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${PROJECT_SOURCE_DIR}/tools/lint/yanglint.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
target_include_directories(yanglint BEFORE PRIVATE ${PROJECT_BINARY_DIR})
if(WIN32)
target_include_directories(yanglint PRIVATE ${GETOPT_INCLUDE_DIR})
target_link_libraries(yanglint ${GETOPT_LIBRARY})
endif()

112
tools/lint/cmd.c Normal file
View file

@ -0,0 +1,112 @@
/**
* @file cmd.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool general commands
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "common.h"
#include "compat.h"
#include "libyang.h"
COMMAND commands[];
extern int done;
void
cmd_free(void)
{
uint16_t i;
for (i = 0; commands[i].name; i++) {
if (commands[i].free_func) {
commands[i].free_func();
}
}
}
int
cmd_quit_exec(struct ly_ctx **UNUSED(ctx), struct yl_opt *UNUSED(yo), const char *UNUSED(posv))
{
done = 1;
return 0;
}
/* Also keep enum COMMAND_INDEX updated. */
COMMAND commands[] = {
{
"help", cmd_help_opt, NULL, cmd_help_exec, NULL, cmd_help_help, NULL,
"Display commands description", "h"
},
{
"add", cmd_add_opt, cmd_add_dep, cmd_add_exec, NULL, cmd_add_help, NULL,
"Add a new module from a specific file", "DF:hiX"
},
{
"load", cmd_load_opt, cmd_load_dep, cmd_load_exec, NULL, cmd_load_help, NULL,
"Load a new schema from the searchdirs", "F:hiX"
},
{
"print", cmd_print_opt, cmd_print_dep, cmd_print_exec, NULL, cmd_print_help, NULL,
"Print a module", "f:hL:o:P:q"
},
{
"data", cmd_data_opt, cmd_data_dep, cmd_data_store, cmd_data_process, cmd_data_help, NULL,
"Load, validate and optionally print instance data", "d:ef:F:hmo:O:R:r:nt:x:"
},
{
"list", cmd_list_opt, cmd_list_dep, cmd_list_exec, NULL, cmd_list_help, NULL,
"List all the loaded modules", "f:h"
},
{
"feature", cmd_feature_opt, cmd_feature_dep, cmd_feature_exec, cmd_feature_fin, cmd_feature_help, NULL,
"Print all features of module(s) with their state", "haf"
},
{
"searchpath", cmd_searchpath_opt, NULL, cmd_searchpath_exec, NULL, cmd_searchpath_help, NULL,
"Print/set the search path(s) for schemas", "ch"
},
{
"extdata", cmd_extdata_opt, cmd_extdata_dep, cmd_extdata_exec, NULL, cmd_extdata_help, cmd_extdata_free,
"Set the specific data required by an extension", "ch"
},
{
"clear", cmd_clear_opt, cmd_clear_dep, cmd_clear_exec, NULL, cmd_clear_help, NULL,
"Clear the context - remove all the loaded modules", "iyhY:"
},
{
"verb", cmd_verb_opt, cmd_verb_dep, cmd_verb_exec, NULL, cmd_verb_help, NULL,
"Change verbosity", "h"
},
{
"debug", cmd_debug_opt, cmd_debug_dep, cmd_debug_store, cmd_debug_setlog, cmd_debug_help, NULL,
#ifndef NDEBUG
"Display specific debug message groups", "h"
#else
"Unsupported for the Release build", "h"
#endif
},
{"quit", NULL, NULL, cmd_quit_exec, NULL, NULL, NULL, "Quit the program", "h"},
/* synonyms for previous commands */
{"?", NULL, NULL, cmd_help_exec, NULL, NULL, NULL, "Display commands description", "h"},
{"exit", NULL, NULL, cmd_quit_exec, NULL, NULL, NULL, "Quit the program", "h"},
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};

394
tools/lint/cmd.h Normal file
View file

@ -0,0 +1,394 @@
/**
* @file cmd.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool commands header
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef COMMANDS_H_
#define COMMANDS_H_
#include "libyang.h"
struct yl_opt;
/**
* @brief command information
*
* Callback functions are in the order they should be called.
* First, the 'opt_func' should be called, which parses arguments from the command line and sets flags or pointers in
* the struct yl_opt. This type of function is for interactive mode and is optional.
* Then the 'dep_func' callback can check the struct yl_opt settings. Other items that depend on them can also be
* set. There is also an possibility for controlling the number of positional arguments and its implications.
* The most important callback is 'exec_func' where the command itself is executed. This function can even replace the
* entire libyang context. The function parameters are mainly found in the yl_opt structure. Optionally, the function
* can be called with a positional argument obtained from the command line. Some 'exec_func' are adapted to be called
* from non-interactive yanglint mode.
* The 'fun_func' complements the 'exec_func'. In some cases, the command execution must be divided into two stages.
* For example, the 'exec_func' is used to fill some items in the yl_opt structure from the positional
* arguments and then the 'fin_func' is used to perform the final action.
*/
typedef struct {
/* User printable name of the function. */
char *name;
/* Convert command line options to the data struct yl_opt. */
int (*opt_func)(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/* Additionally set dependent items and perform error checking. */
int (*dep_func)(struct yl_opt *yo, int posc);
/* Execute command. */
int (*exec_func)(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
/* Finish execution of command. */
int (*fin_func)(struct ly_ctx *ctx, struct yl_opt *yo);
/* Display command help. */
void (*help_func)(void);
/* Freeing global variables allocated by the command. */
void (*free_func)(void);
/* Documentation for this function. */
char *helpstring;
/* Option characters used in function getopt_long. */
char *optstring;
} COMMAND;
/**
* @brief The list of available commands.
*/
extern COMMAND commands[];
/**
* @brief Index for global variable ::commands.
*/
enum COMMAND_INDEX {
CMD_HELP = 0,
CMD_ADD,
CMD_LOAD,
CMD_PRINT,
CMD_DATA,
CMD_LIST,
CMD_FEATURE,
CMD_SEARCHPATH,
CMD_EXTDATA,
CMD_CLEAR,
CMD_VERB,
CMD_DEBUG
};
/**
* @brief For each cmd, call the COMMAND.free_func in the variable 'commands'.
*/
void cmd_free(void);
/* cmd_add.c */
/**
* @brief Parse the arguments of an interactive command.
*
* @param[out] yo Context for yanglint.
* @param[in] cmdline String containing command line arguments.
* @param[out] posv Pointer to argv to a section of positional arguments.
* @param[out] posc Number of positional arguments.
* @return 0 on success.
*/
int cmd_add_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @brief Check the options and set dependent items in @p yo.
*
* @param[in,out] yo context for yanglint.
* @param[in] posc number of positional arguments.
* @return 0 on success.
*/
int cmd_add_dep(struct yl_opt *yo, int posc);
/**
* @brief Parse and compile a new module using filepath.
*
* @param[in,out] ctx Context for libyang.
* @param[in,out] yo Context for yanglint.
* @param[in] posv Path to the file where the new module is located.
* @return 0 on success.
*/
int cmd_add_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_add_help(void);
/* cmd_clear.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_clear_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_clear_dep(struct yl_opt *yo, int posc);
/**
* @brief Clear libyang context.
*
* @param[in,out] ctx context for libyang that will be replaced with an empty one.
* @param[in,out] yo context for yanglint.
* @param[in] posv not used.
* @return 0 on success.
*/
int cmd_clear_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_clear_help(void);
/* cmd_data.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_data_dep(struct yl_opt *yo, int posc);
/**
* @brief Store data file for later processing.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Path to the file where the data is located.
* @return 0 on success.
*/
int cmd_data_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
/**
* @brief Parse, validate and optionally print data instances.
*
* @param[in] ctx Context for libyang.
* @param[in] yo Context of yanglint. All necessary parameters should already be set.
* @return 0 on success.
*/
int cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_data_help(void);
/* cmd_list.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_list_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_list_dep(struct yl_opt *yo, int posc);
/**
* @brief Print the list of modules in the current context.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Not used.
* @return 0 on success.
*/
int cmd_list_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_list_help(void);
/* cmd_feature.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_feature_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_feature_dep(struct yl_opt *yo, int posc);
/**
* @brief Print the features the modules.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Name of the module which features are to be printed.
* @return 0 on success.
*/
int cmd_feature_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
/**
* @brief Printing of features ends.
*
* @param[in] ctx context for libyang. Not used.
* @param[in] yo context for yanglint.
* @return 0 on success.
*/
int cmd_feature_fin(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_feature_help(void);
/* cmd_load.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_load_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_load_dep(struct yl_opt *yo, int posc);
/**
* @brief Parse and compile a new module using module name.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Name of the module to be loaded into the context.
* @return 0 on success.
*/
int cmd_load_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_load_help(void);
/* cmd_print.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_print_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_print_dep(struct yl_opt *yo, int posc);
/**
* @brief Print a schema module.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Name of the module to be printed. Can be NULL in the case of printing a node.
* @return 0 on success.
*/
int cmd_print_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_print_help(void);
/* cmd_searchpath.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_searchpath_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @brief Set the paths of directories where to search schema modules.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Path to the directory. Can be NULL in the case of printing a current searchdirs.
* @return 0 on success.
*/
int cmd_searchpath_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_searchpath_help(void);
/* cmd_extdata.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_extdata_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_extdata_dep(struct yl_opt *yo, int posc);
/**
* @brief Set path to the file required by the extension.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Path to the directory. Can be NULL in the case of printing a current path.
* @return 0 on success.
*/
int cmd_extdata_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_extdata_help(void);
void cmd_extdata_free(void);
/* cmd_help.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_help_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @brief Print help.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Name of the command which help message is to be printed. Can be NULL.
* @return 0 on success.
*/
int cmd_help_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_help_help(void);
/* cmd_verb.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_verb_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_verb_dep(struct yl_opt *yo, int posc);
/**
* @brief Set the verbose level.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Name of the verbose level to be set.
* @return 0 on success.
*/
int cmd_verb_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_verb_help(void);
/* cmd_debug.c */
/**
* @copydoc cmd_add_opt
*/
int cmd_debug_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
/**
* @copydoc cmd_add_dep
*/
int cmd_debug_dep(struct yl_opt *yo, int posc);
/**
* @brief Store the type of debug messages for later processing.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Name of the debug type to be set.
* @return 0 on success.
*/
int cmd_debug_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
/**
* @brief Set debug logging.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint. All necessary parameters should already be set.
* @return 0 on success.
*/
int cmd_debug_setlog(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_debug_help(void);
#endif /* COMMANDS_H_ */

210
tools/lint/cmd_add.c Normal file
View file

@ -0,0 +1,210 @@
/**
* @file cmd_add.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'add' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <assert.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
#include "yl_schema_features.h"
void
cmd_add_help(void)
{
printf("Usage: add [-iD] <schema1> [<schema2> ...]\n"
" Add a new module from a specific file.\n\n"
" -D, --disable-searchdir\n"
" Do not implicitly search in current working directory for\n"
" the import schema modules. If specified a second time, do not\n"
" even search in the module directory (all modules must be \n"
" explicitly specified).\n"
" -F FEATURES, --features=FEATURES\n"
" Features to support, default all in all implemented modules.\n"
" Specify separately for each module.\n"
" <modname>:[<feature>,]*\n"
" -i, --make-implemented\n"
" Make the imported modules \"referenced\" from any loaded\n"
" <schema> module also implemented. If specified a second time,\n"
" all the modules are set implemented.\n"
" -X, --extended-leafref\n"
" Allow usage of deref() XPath function within leafref.\n");
}
int
cmd_add_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"disable-searchdir", no_argument, NULL, 'D'},
{"features", required_argument, NULL, 'F'},
{"help", no_argument, NULL, 'h'},
{"make-implemented", no_argument, NULL, 'i'},
{"extended-leafref", no_argument, NULL, 'X'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_ADD].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'D': /* --disable--search */
if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
YLMSG_W("The -D option specified too many times.");
}
yo_opt_update_disable_searchdir(yo);
break;
case 'F': /* --features */
if (parse_features(optarg, &yo->schema_features)) {
return 1;
}
break;
case 'h':
cmd_add_help();
return 1;
case 'i': /* --make-implemented */
yo_opt_update_make_implemented(yo);
break;
case 'X': /* --extended-leafref */
yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
break;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_add_dep(struct yl_opt *yo, int posc)
{
if (yo->interactive && !posc) {
/* no argument */
cmd_add_help();
return 1;
}
if (!yo->schema_features.count) {
/* no features, enable all of them */
yo->ctx_options |= LY_CTX_ENABLE_IMP_FEATURES;
}
return 0;
}
static int
store_parsed_module(const char *filepath, struct lys_module *mod, struct yl_opt *yo)
{
assert(!yo->interactive);
if (yo->schema_out_format || yo->feature_param_format) {
if (ly_set_add(&yo->schema_modules, (void *)mod, 1, NULL)) {
YLMSG_E("Storing parsed schema module (%s) for print failed.", filepath);
return 1;
}
}
return 0;
}
int
cmd_add_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
const char *all_features[] = {"*", NULL};
LY_ERR ret;
uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
char *dir, *modname = NULL;
const char **features = NULL;
struct ly_in *in = NULL;
struct lys_module *mod;
assert(posv);
if (yo->ctx_options) {
ly_ctx_set_options(*ctx, yo->ctx_options);
}
if (parse_schema_path(posv, &dir, &modname)) {
return 1;
}
if (!yo->interactive) {
/* The module should already be parsed if yang_lib_file was set. */
if (yo->yang_lib_file && (mod = ly_ctx_get_module_implemented(*ctx, modname))) {
ret = store_parsed_module(posv, mod, yo);
goto cleanup;
}
/* parse module */
}
/* add temporarily also the path of the module itself */
if (ly_ctx_set_searchdir(*ctx, dir) == LY_EEXIST) {
path_unset = 0;
}
/* get features list for this module */
if (!yo->schema_features.count) {
features = all_features;
} else {
get_features(&yo->schema_features, modname, &features);
}
/* prepare input handler */
ret = ly_in_new_filepath(posv, 0, &in);
if (ret) {
goto cleanup;
}
/* parse the file */
ret = lys_parse(*ctx, in, LYS_IN_UNKNOWN, features, &mod);
ly_in_free(in, 1);
ly_ctx_unset_searchdir_last(*ctx, path_unset);
if (ret) {
goto cleanup;
}
if (!yo->interactive) {
if ((ret = store_parsed_module(posv, mod, yo))) {
goto cleanup;
}
}
cleanup:
free(dir);
free(modname);
return ret;
}

172
tools/lint/cmd_clear.c Normal file
View file

@ -0,0 +1,172 @@
/**
* @file cmd_clear.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'clear' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
void
cmd_clear_help(void)
{
printf("Usage: clear [-i] [-y]\n"
" Replace the current context with an empty one, searchpaths\n"
" are not kept.\n\n"
" -i, --make-implemented\n"
" When loading a module into the context, the imported 'referenced'\n"
" modules will also be implemented. If specified a second time,\n"
" all the modules will be implemented.\n"
" -y, --yang-library\n"
" Load and implement internal \"ietf-yang-library\" YANG module.\n"
" Note that this module includes definitions of mandatory state\n"
" data that can result in unexpected data validation errors.\n"
" -Y FILE, --yang-library-file=FILE\n"
" Parse FILE with \"ietf-yang-library\" data and use them to\n"
" create an exact YANG schema context. Searchpaths defined so far\n"
" are used, but then deleted.\n");
}
int
cmd_clear_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"make-implemented", no_argument, NULL, 'i'},
{"yang-library", no_argument, NULL, 'y'},
{"yang-library-file", no_argument, NULL, 'Y'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
yo->ctx_options = LY_CTX_NO_YANGLIBRARY;
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_CLEAR].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'i':
if (yo->ctx_options & LY_CTX_REF_IMPLEMENTED) {
yo->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
yo->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
} else {
yo->ctx_options |= LY_CTX_REF_IMPLEMENTED;
}
break;
case 'y':
yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
break;
case 'Y':
yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
yo->yang_lib_file = optarg;
if (!yo->yang_lib_file) {
YLMSG_E("Memory allocation error.");
return 1;
}
break;
case 'h':
cmd_clear_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return rc;
}
int
cmd_clear_dep(struct yl_opt *yo, int posc)
{
(void) yo;
if (posc) {
YLMSG_E("No positional arguments are allowed.");
return 1;
}
return 0;
}
/**
* @brief Convert searchpaths into single string.
*
* @param[in] ctx Context with searchpaths.
* @param[out] searchpaths Collection of paths in the single string. Paths are delimited by colon ":"
* (on Windows, used semicolon ";" instead).
* @return LY_ERR value.
*/
static LY_ERR
searchpaths_to_str(const struct ly_ctx *ctx, char **searchpaths)
{
uint32_t i;
int rc = 0;
const char * const *dirs = ly_ctx_get_searchdirs(ctx);
for (i = 0; dirs[i]; ++i) {
rc = searchpath_strcat(searchpaths, dirs[i]);
if (!rc) {
break;
}
}
return rc;
}
int
cmd_clear_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
(void) posv;
struct ly_ctx *ctx_new = NULL;
if (yo->yang_lib_file) {
if (searchpaths_to_str(*ctx, &yo->searchpaths)) {
YLMSG_E("Storing searchpaths failed.");
return 1;
}
if (ly_ctx_new_ylpath(yo->searchpaths, yo->yang_lib_file, LYD_UNKNOWN, yo->ctx_options, &ctx_new)) {
YLMSG_E("Unable to create libyang context with yang-library data.");
return 1;
}
} else {
if (ly_ctx_new(NULL, yo->ctx_options, &ctx_new)) {
YLMSG_W("Failed to create context.");
return 1;
}
}
/* Global variables in commands are also deleted. */
cmd_free();
ly_ctx_destroy(*ctx);
*ctx = ctx_new;
return 0;
}

686
tools/lint/cmd_data.c Normal file
View file

@ -0,0 +1,686 @@
/**
* @file cmd_data.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'data' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
static void
cmd_data_help_header(void)
{
printf("Usage: data [-emn] [-t TYPE]\n"
" [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
" data [-n] -t (rpc | notif | reply) [-O FILE]\n"
" [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
" data [-en] [-t TYPE] [-F FORMAT] -x XPATH [-o OUTFILE] <data1> ...\n"
" Parse, validate and optionally print data instances\n");
}
static void
cmd_data_help_type(void)
{
printf(" -t TYPE, --type=TYPE\n"
" Specify data tree type in the input data file(s):\n"
" data - Complete datastore with status data (default type).\n"
" config - Configuration datastore (without status data).\n"
" get - Result of the NETCONF <get> operation.\n"
" getconfig - Result of the NETCONF <get-config> operation.\n"
" edit - Content of the NETCONF <edit-config> operation.\n"
" rpc - Content of the NETCONF <rpc> message, defined as YANG's\n"
" RPC/Action input statement.\n"
" nc-rpc - Similar to 'rpc' but expect and check also the NETCONF\n"
" envelopes <rpc> or <action>.\n"
" reply - Reply to the RPC/Action. Note that the reply data are\n"
" expected inside a container representing the original\n"
" RPC/Action. This is necessary to identify appropriate\n"
" data definitions in the schema module.\n"
" nc-reply - Similar to 'reply' but expect and check also the NETCONF\n"
" envelope <rpc-reply> with output data nodes as direct\n"
" descendants. The original RPC/action invocation is expected\n"
" in a separate parameter '-R' and is parsed as 'nc-rpc'.\n"
" notif - Notification instance (content of the <notification>\n"
" element without <eventTime>).\n"
" nc-notif - Similar to 'notif' but expect and check also the NETCONF\n"
" envelope <notification> with element <eventTime> and its\n"
" sibling as the actual notification.\n");
}
static void
cmd_data_help_format(void)
{
printf(" -f FORMAT, --format=FORMAT\n"
" Print the data in one of the following formats:\n"
" xml, json, lyb\n"
" Note that the LYB format requires the -o option specified.\n");
}
static void
cmd_data_help_in_format(void)
{
printf(" -F FORMAT, --in-format=FORMAT\n"
" Load the data in one of the following formats:\n"
" xml, json, lyb\n"
" If input format not specified, it is detected from the file extension.\n");
}
static void
cmd_data_help_default(void)
{
printf(" -d MODE, --default=MODE\n"
" Print data with default values, according to the MODE\n"
" (to print attributes, ietf-netconf-with-defaults model\n"
" must be loaded):\n"
" all - Add missing default nodes.\n"
" all-tagged - Add missing default nodes and mark all the default\n"
" nodes with the attribute.\n"
" trim - Remove all nodes with a default value.\n"
" implicit-tagged - Add missing nodes and mark them with the attribute.\n");
}
static void
cmd_data_help_xpath(void)
{
printf(" -x XPATH, --xpath=XPATH\n"
" Evaluate XPATH expression and print the nodes satisfying the\n"
" expression. The output format is specific and the option cannot\n"
" be combined with the -f and -d options. Also all the data\n"
" inputs are merged into a single data tree where the expression\n"
" is evaluated, so the -m option is always set implicitly.\n");
}
void
cmd_data_help(void)
{
cmd_data_help_header();
printf("\n");
cmd_data_help_type();
printf(" -e, --present Validate only with the schema modules whose data actually\n"
" exist in the provided input data files. Takes effect only\n"
" with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
" mandatory nodes from modules which data are not present in the\n"
" provided input data files.\n"
" -m, --merge Merge input data files into a single tree and validate at\n"
" once.The option has effect only for 'data' and 'config' TYPEs.\n"
" In case of using -x option, the data are always merged.\n"
" -n, --not-strict\n"
" Do not require strict data parsing (silently skip unknown data),\n"
" has no effect for schemas.\n"
" -O FILE, --operational=FILE\n"
" Provide optional data to extend validation of the 'rpc',\n"
" 'reply' or 'notif' TYPEs. The FILE is supposed to contain\n"
" the operational datastore referenced from the operation.\n"
" In case of a nested notification or action, its parent\n"
" existence is also checked in these operational data.\n"
" -R FILE, --reply-rpc=FILE\n"
" Provide source RPC for parsing of the 'nc-reply' TYPE. The FILE\n"
" is supposed to contain the source 'nc-rpc' operation of the reply.\n");
cmd_data_help_format();
cmd_data_help_in_format();
printf(" -o OUTFILE, --output=OUTFILE\n"
" Write the output to OUTFILE instead of stdout.\n");
cmd_data_help_xpath();
printf("\n");
}
int
cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"defaults", required_argument, NULL, 'd'},
{"present", no_argument, NULL, 'e'},
{"format", required_argument, NULL, 'f'},
{"in-format", required_argument, NULL, 'F'},
{"help", no_argument, NULL, 'h'},
{"merge", no_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
{"operational", required_argument, NULL, 'O'},
{"reply-rpc", required_argument, NULL, 'R'},
{"not-strict", no_argument, NULL, 'n'},
{"type", required_argument, NULL, 't'},
{"xpath", required_argument, NULL, 'x'},
{NULL, 0, NULL, 0}
};
uint8_t data_type_set = 0;
yo->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
yo->data_validate_options = YL_DEFAULT_DATA_VALIDATE_OPTIONS;
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_DATA].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'd': /* --default */
if (yo_opt_update_data_default(optarg, yo)) {
YLMSG_E("Unknown default mode %s.", optarg);
cmd_data_help_default();
return 1;
}
break;
case 'f': /* --format */
if (yl_opt_update_data_out_format(optarg, yo)) {
cmd_data_help_format();
return 1;
}
break;
case 'F': /* --in-format */
if (yo_opt_update_data_in_format(optarg, yo)) {
YLMSG_E("Unknown input format %s.", optarg);
cmd_data_help_in_format();
return 1;
}
break;
case 'o': /* --output */
if (yo->out) {
YLMSG_E("Only a single output can be specified.");
return 1;
} else {
if (ly_out_new_filepath(optarg, &yo->out)) {
YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
return 1;
}
}
break;
case 'O': /* --operational */
if (yo->data_operational.path) {
YLMSG_E("The operational datastore (-O) cannot be set multiple times.");
return 1;
}
yo->data_operational.path = optarg;
break;
case 'R': /* --reply-rpc */
if (yo->reply_rpc.path) {
YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.");
return 1;
}
yo->reply_rpc.path = optarg;
break;
case 'e': /* --present */
yo->data_validate_options |= LYD_VALIDATE_PRESENT;
break;
case 'm': /* --merge */
yo->data_merge = 1;
break;
case 'n': /* --not-strict */
yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
case 't': /* --type */
if (data_type_set) {
YLMSG_E("The data type (-t) cannot be set multiple times.");
return 1;
}
if (yl_opt_update_data_type(optarg, yo)) {
YLMSG_E("Unknown data tree type %s.", optarg);
cmd_data_help_type();
return 1;
}
data_type_set = 1;
break;
case 'x': /* --xpath */
if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
YLMSG_E("Storing XPath \"%s\" failed.", optarg);
return 1;
}
break;
case 'h': /* --help */
cmd_data_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return rc;
}
int
cmd_data_dep(struct yl_opt *yo, int posc)
{
if (yo->interactive && !posc) {
YLMSG_E("Missing the data file to process.");
return 1;
}
if (yo->data_merge) {
if (yo->data_type || (yo->data_parse_options & LYD_PARSE_ONLY)) {
/* switch off the option, incompatible input data type */
YLMSG_W("The --merge option has effect only for 'data' and 'config' TYPEs.");
yo->data_merge = 0;
} else {
/* postpone validation after the merge of all the input data */
yo->data_parse_options |= LYD_PARSE_ONLY;
}
} else if (yo->data_xpath.count) {
yo->data_merge = 1;
}
if (yo->data_xpath.count && (yo->schema_out_format || yo->data_out_format)) {
YLMSG_E("The --format option cannot be combined with --xpath option.");
if (yo->interactive) {
cmd_data_help_xpath();
}
return 1;
}
if (yo->data_xpath.count && (yo->data_print_options & LYD_PRINT_WD_MASK)) {
YLMSG_E("The --default option cannot be combined with --xpath option.");
if (yo->interactive) {
cmd_data_help_xpath();
}
return 1;
}
if (yo->data_operational.path && !yo->data_type) {
YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types.");
yo->data_operational.path = NULL;
}
if (yo->reply_rpc.path && (yo->data_type != LYD_TYPE_REPLY_NETCONF)) {
YLMSG_W("Source RPC is needed only for NETCONF Reply input data type.");
yo->data_operational.path = NULL;
} else if (!yo->reply_rpc.path && (yo->data_type == LYD_TYPE_REPLY_NETCONF)) {
YLMSG_E("Missing source RPC (-R) for NETCONF Reply input data type.");
return 1;
}
if (!yo->out && (yo->data_out_format == LYD_LYB)) {
YLMSG_E("The LYB format requires the -o option specified.");
return 1;
}
/* default output stream */
if (!yo->out) {
if (ly_out_new_file(stdout, &yo->out)) {
YLMSG_E("Unable to set stdout as output.");
return 1;
}
yo->out_stdout = 1;
}
/* process the operational and/or reply RPC content if any */
if (yo->data_operational.path) {
if (get_input(yo->data_operational.path, NULL, &yo->data_operational.format, &yo->data_operational.in)) {
return -1;
}
}
if (yo->reply_rpc.path) {
if (get_input(yo->reply_rpc.path, NULL, &yo->reply_rpc.format, &yo->reply_rpc.in)) {
return -1;
}
}
return 0;
}
int
cmd_data_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
(void) ctx;
struct ly_in *in;
LYD_FORMAT format_data;
assert(posv);
format_data = yo->data_in_format;
/* process input data files provided as standalone command line arguments */
if (get_input(posv, NULL, &format_data, &in)) {
return 1;
}
if (!fill_cmdline_file(&yo->data_inputs, in, posv, format_data)) {
ly_in_free(in, 1);
return 1;
}
return 0;
}
/**
* @brief Evaluate xpath adn print result.
*
* @param[in] tree Data tree.
* @param[in] xpath Xpath to evaluate.
* @return 0 on success.
*/
static int
evaluate_xpath(const struct lyd_node *tree, const char *xpath)
{
struct ly_set *set = NULL;
if (lyd_find_xpath(tree, xpath, &set)) {
return -1;
}
/* print result */
printf("XPath \"%s\" evaluation result:\n", xpath);
if (!set->count) {
printf("\tEmpty\n");
} else {
for (uint32_t u = 0; u < set->count; ++u) {
struct lyd_node *node = (struct lyd_node *)set->objs[u];
printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
printf(" (value: \"%s\")\n", lyd_get_value(node));
} else if (node->schema->nodetype == LYS_LIST) {
printf(" (");
for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
key->schema->name, lyd_get_value(key));
}
printf(")\n");
} else {
printf("\n");
}
}
}
ly_set_free(set, NULL);
return 0;
}
/**
* @brief Checking that a parent data node exists in the datastore for the nested-notification and action.
*
* @param[in] op Operation to check.
* @param[in] oper_tree Data from datastore.
* @param[in] operational_f Operational datastore file information.
* @return LY_ERR value.
*/
static LY_ERR
check_operation_parent(struct lyd_node *op, struct lyd_node *oper_tree, struct cmdline_file *operational_f)
{
LY_ERR ret;
struct ly_set *set = NULL;
char *path = NULL;
if (!op || !lyd_parent(op)) {
/* The function is defined only for nested-notification and action. */
return LY_SUCCESS;
}
if (!operational_f || (operational_f && !operational_f->in)) {
YLMSG_E("The --operational parameter needed to validate operation \"%s\" is missing.", LYD_NAME(op));
ret = LY_EVALID;
goto cleanup;
}
path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
if (!path) {
ret = LY_EMEM;
goto cleanup;
}
if (!oper_tree) {
YLMSG_W("Operational datastore is empty or contains unknown data.");
YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.", LYD_NAME(op), path);
ret = LY_EVALID;
goto cleanup;
}
if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
goto cleanup;
}
if (!set->count) {
YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.", LYD_NAME(op), path);
ret = LY_EVALID;
goto cleanup;
}
cleanup:
ly_set_free(set, NULL);
free(path);
return ret;
}
/**
* @brief Process the input data files - parse, validate and print according to provided options.
*
* @param[in] ctx libyang context with schema.
* @param[in] type The type of data in the input files.
* @param[in] merge Flag if the data should be merged before validation.
* @param[in] out_format Data format for printing.
* @param[in] out The output handler for printing.
* @param[in] parse_options Parser options.
* @param[in] validate_options Validation options.
* @param[in] print_options Printer options.
* @param[in] operational Optional operational datastore file information for the case of an extended validation of
* operation(s).
* @param[in] reply_rpc Source RPC operation file information for parsing NETCONF rpc-reply.
* @param[in] inputs Set of file informations of input data files.
* @param[in] xpaths The set of XPaths to be evaluated on the processed data tree, basic information about the resulting set
* is printed. Alternative to data printing.
* @return LY_ERR value.
*/
static LY_ERR
process_data(struct ly_ctx *ctx, enum lyd_type type, uint8_t merge, LYD_FORMAT out_format,
struct ly_out *out, uint32_t parse_options, uint32_t validate_options, uint32_t print_options,
struct cmdline_file *operational, struct cmdline_file *reply_rpc, struct ly_set *inputs,
struct ly_set *xpaths)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
const char *xpath;
struct ly_set *set = NULL;
/* additional operational datastore */
if (operational && operational->in) {
ret = lyd_parse_data(ctx, NULL, operational->in, operational->format, LYD_PARSE_ONLY, 0, &oper_tree);
if (ret) {
YLMSG_E("Failed to parse operational datastore file \"%s\".", operational->path);
goto cleanup;
}
}
for (uint32_t u = 0; u < inputs->count; ++u) {
struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
switch (type) {
case LYD_TYPE_DATA_YANG:
ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, parse_options, validate_options, &tree);
break;
case LYD_TYPE_RPC_YANG:
case LYD_TYPE_REPLY_YANG:
case LYD_TYPE_NOTIF_YANG:
ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, &tree, &op);
break;
case LYD_TYPE_RPC_NETCONF:
case LYD_TYPE_NOTIF_NETCONF:
ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, &envp, &op);
/* adjust pointers */
for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
break;
case LYD_TYPE_REPLY_NETCONF:
/* parse source RPC operation */
assert(reply_rpc && reply_rpc->in);
ret = lyd_parse_op(ctx, NULL, reply_rpc->in, reply_rpc->format, LYD_TYPE_RPC_NETCONF, &envp, &op);
if (ret) {
YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".", reply_rpc->path);
goto cleanup;
}
/* adjust pointers */
for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
/* free input */
lyd_free_siblings(lyd_child(op));
/* we do not care */
lyd_free_all(envp);
envp = NULL;
ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, type, &envp, NULL);
break;
default:
YLMSG_E("Internal error (%s:%d).", __FILE__, __LINE__);
goto cleanup;
}
if (ret) {
YLMSG_E("Failed to parse input data file \"%s\".", input_f->path);
goto cleanup;
}
if (merge) {
/* merge the data so far parsed for later validation and print */
if (!merged_tree) {
merged_tree = tree;
} else {
ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
if (ret) {
YLMSG_E("Merging %s with previous data failed.", input_f->path);
goto cleanup;
}
}
tree = NULL;
} else if (out_format) {
/* print */
switch (type) {
case LYD_TYPE_DATA_YANG:
lyd_print_all(out, tree, out_format, print_options);
break;
case LYD_TYPE_RPC_YANG:
case LYD_TYPE_REPLY_YANG:
case LYD_TYPE_NOTIF_YANG:
case LYD_TYPE_RPC_NETCONF:
case LYD_TYPE_NOTIF_NETCONF:
lyd_print_tree(out, tree, out_format, print_options);
break;
case LYD_TYPE_REPLY_NETCONF:
/* just the output */
lyd_print_tree(out, lyd_child(tree), out_format, print_options);
break;
default:
assert(0);
}
} else {
/* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
switch (type) {
case LYD_TYPE_DATA_YANG:
/* already validated */
break;
case LYD_TYPE_RPC_YANG:
case LYD_TYPE_RPC_NETCONF:
ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
break;
case LYD_TYPE_REPLY_YANG:
case LYD_TYPE_REPLY_NETCONF:
ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
break;
case LYD_TYPE_NOTIF_YANG:
case LYD_TYPE_NOTIF_NETCONF:
ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
break;
default:
assert(0);
}
if (ret) {
if (operational->path) {
YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".",
input_f->path, operational->path);
} else {
YLMSG_E("Failed to validate input data file \"%s\".", input_f->path);
}
goto cleanup;
}
if ((ret = check_operation_parent(op, oper_tree, operational))) {
goto cleanup;
}
}
/* next iter */
lyd_free_all(tree);
tree = NULL;
lyd_free_all(envp);
envp = NULL;
}
if (merge) {
/* validate the merged result */
ret = lyd_validate_all(&merged_tree, ctx, validate_options, NULL);
if (ret) {
YLMSG_E("Merged data are not valid.");
goto cleanup;
}
if (out_format) {
/* and print it */
lyd_print_all(out, merged_tree, out_format, print_options);
}
for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
xpath = (const char *)xpaths->objs[u];
ly_set_free(set, NULL);
ret = lys_find_xpath(ctx, NULL, xpath, LYS_FIND_NO_MATCH_ERROR, &set);
if (ret || !set->count) {
ret = (ret == LY_SUCCESS) ? LY_EINVAL : ret;
YLMSG_E("The requested xpath failed.");
goto cleanup;
}
if (evaluate_xpath(merged_tree, xpath)) {
goto cleanup;
}
}
}
cleanup:
lyd_free_all(tree);
lyd_free_all(envp);
lyd_free_all(merged_tree);
lyd_free_all(oper_tree);
ly_set_free(set, NULL);
return ret;
}
int
cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo)
{
/* parse, validate and print data */
if (process_data(ctx, yo->data_type, yo->data_merge, yo->data_out_format, yo->out, yo->data_parse_options,
yo->data_validate_options, yo->data_print_options, &yo->data_operational, &yo->reply_rpc,
&yo->data_inputs, &yo->data_xpath)) {
return 1;
}
return 0;
}

130
tools/lint/cmd_debug.c Normal file
View file

@ -0,0 +1,130 @@
/**
* @file cmd_debug.c
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'verb' command of the libyang's yanglint tool.
*
* Copyright (c) 2023-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include "cmd.h"
#include <assert.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <strings.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
struct debug_groups {
char *name;
uint32_t flag;
} const dg [] = {
{"dict", LY_LDGDICT},
{"xpath", LY_LDGXPATH},
{"dep-sets", LY_LDGDEPSETS},
};
#define DG_LENGTH (sizeof dg / sizeof *dg)
void
cmd_debug_help(void)
{
uint32_t i;
printf("Usage: debug (");
for (i = 0; i < DG_LENGTH; i++) {
if ((i + 1) == DG_LENGTH) {
printf("%s", dg[i].name);
} else {
printf("%s | ", dg[i].name);
}
}
printf(")+\n");
}
int
cmd_debug_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_DEBUG].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'h':
cmd_debug_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_debug_dep(struct yl_opt *yo, int posc)
{
(void) yo;
if (yo->interactive && !posc) {
/* no argument */
cmd_debug_help();
return 1;
}
return 0;
}
int
cmd_debug_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
(void) ctx;
uint32_t i;
ly_bool set;
assert(posv);
set = 0;
for (i = 0; i < DG_LENGTH; i++) {
if (!strcasecmp(posv, dg[i].name)) {
yo->dbg_groups |= dg[i].flag;
set = 1;
break;
}
}
if (!set) {
YLMSG_E("Unknown debug group \"%s\".", posv);
return 1;
}
return 0;
}
int
cmd_debug_setlog(struct ly_ctx *ctx, struct yl_opt *yo)
{
(void) ctx;
return ly_log_dbg_groups(yo->dbg_groups);
}

115
tools/lint/cmd_extdata.c Normal file
View file

@ -0,0 +1,115 @@
/**
* @file cmd_extdata.c
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'extdata' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L /* strdup */
#include "cmd.h"
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
char *filename;
void
cmd_extdata_free(void)
{
free(filename);
filename = NULL;
}
void
cmd_extdata_help(void)
{
printf("Usage: extdata [--clear] [<extdata-file-path>]\n"
" File containing the specific data required by an extension. Required by\n"
" the schema-mount extension, for example, when the operational data are\n"
" expected in the file. File format is guessed.\n");
}
int
cmd_extdata_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"clear", no_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_EXTDATA].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'c':
yo->extdata_unset = 1;
free(filename);
filename = NULL;
break;
case 'h':
cmd_extdata_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_extdata_dep(struct yl_opt *yo, int posc)
{
if (!yo->extdata_unset && (posc > 1)) {
YLMSG_E("Only one file must be entered.");
return 1;
}
return 0;
}
int
cmd_extdata_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
if (yo->extdata_unset) {
ly_ctx_set_ext_data_clb(*ctx, NULL, NULL);
} else if (!yo->extdata_unset && !posv) {
/* no argument - print the current file */
printf("%s\n", filename ? filename : "No file set.");
} else if (posv) {
/* set callback providing run-time extension instance data */
free(filename);
filename = strdup(posv);
if (!filename) {
YLMSG_E("Memory allocation error.");
return 1;
}
ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, filename);
}
return 0;
}

131
tools/lint/cmd_feature.c Normal file
View file

@ -0,0 +1,131 @@
/**
* @file cmd_feature.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'feature' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
#include "yl_schema_features.h"
void
cmd_feature_help(void)
{
printf("Usage: feature [-f] <module> [<module>]*\n"
" feature -a [-f]\n"
" Print features of all the modules with state of each one.\n\n"
" -f <module1, module2, ...>, --feature-param <module1, module2, ...>\n"
" Generate features parameter for the command \"add\" \n"
" in the form of -F <module-name>:<features>\n"
" -a, --all \n"
" Print features of all implemented modules.\n");
}
int
cmd_feature_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{"all", no_argument, NULL, 'a'},
{"feature-param", no_argument, NULL, 'f'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_FEATURE].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'h':
cmd_feature_help();
return 1;
case 'a':
yo->feature_print_all = 1;
break;
case 'f':
yo->feature_param_format = 1;
break;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_feature_dep(struct yl_opt *yo, int posc)
{
if (ly_out_new_file(stdout, &yo->out)) {
YLMSG_E("Unable to print to the standard output.");
return 1;
}
yo->out_stdout = 1;
if (yo->interactive && !yo->feature_print_all && !posc) {
YLMSG_E("Missing modules to print.");
return 1;
}
return 0;
}
int
cmd_feature_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
const struct lys_module *mod;
if (yo->feature_print_all) {
print_all_features(yo->out, *ctx, yo->feature_param_format);
return 0;
}
mod = ly_ctx_get_module_latest(*ctx, posv);
if (!mod) {
YLMSG_E("Module \"%s\" not found.", posv);
return 1;
}
if (yo->feature_param_format) {
print_feature_param(yo->out, mod);
} else {
print_features(yo->out, mod);
}
return 0;
}
int
cmd_feature_fin(struct ly_ctx *ctx, struct yl_opt *yo)
{
(void) ctx;
ly_print(yo->out, "\n");
return 0;
}

107
tools/lint/cmd_help.c Normal file
View file

@ -0,0 +1,107 @@
/**
* @file cmd_help.c
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'help' command of the libyang's yanglint tool.
*
* Copyright (c) 2023-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
void
cmd_help_help(void)
{
printf("Usage: help [cmd ...]\n");
}
int
cmd_help_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_HELP].optstring, options, &opt_index)) != -1) {
if (opt == 'h') {
cmd_help_help();
return 1;
} else {
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return rc;
}
static void
print_generic_help(void)
{
printf("Available commands:\n");
for (uint16_t i = 0; commands[i].name; i++) {
if (commands[i].helpstring != NULL) {
printf(" %-15s %s\n", commands[i].name, commands[i].helpstring);
}
}
}
int
cmd_help_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
(void)ctx, (void)yo;
if (!posv) {
print_generic_help();
} else {
/* print specific help for the selected command(s) */
int8_t match = 0;
/* get the command of the specified name */
for (uint16_t i = 0; commands[i].name; i++) {
if (strcmp(posv, commands[i].name) == 0) {
match = 1;
if (commands[i].help_func != NULL) {
commands[i].help_func();
} else {
printf("%s: %s\n", posv, commands[i].helpstring);
}
break;
}
}
if (!match) {
/* if unknown command specified, print the list of commands */
printf("Unknown command \'%s\'\n", posv);
print_generic_help();
}
}
return 0;
}

186
tools/lint/cmd_list.c Normal file
View file

@ -0,0 +1,186 @@
/**
* @file cmd_list.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'list' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <getopt.h>
#include <stdio.h>
#include <strings.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
void
cmd_list_help(void)
{
printf("Usage: list [-f (xml | json)]\n"
" Print the list of modules in the current context\n\n"
" -f FORMAT, --format=FORMAT\n"
" Print the list as ietf-yang-library data in the specified\n"
" data FORMAT. If format not specified, a simple list is\n"
" printed with an indication of imported (i) / implemented (I)\n"
" modules.\n");
}
int
cmd_list_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"format", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
yo->data_out_format = LYD_UNKNOWN;
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_LIST].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'f': /* --format */
if (!strcasecmp(optarg, "xml")) {
yo->data_out_format = LYD_XML;
} else if (!strcasecmp(optarg, "json")) {
yo->data_out_format = LYD_JSON;
} else {
YLMSG_E("Unknown output format %s.", optarg);
cmd_list_help();
return 1;
}
break;
case 'h':
cmd_list_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_list_dep(struct yl_opt *yo, int posc)
{
if (posc) {
YLMSG_E("No positional arguments are allowed.");
return 1;
}
if (ly_out_new_file(stdout, &yo->out)) {
YLMSG_E("Unable to print to the standard output.");
return 1;
}
yo->out_stdout = 1;
return 0;
}
/**
* @brief Print yang library data.
*
* @param[in] ctx Context for libyang.
* @param[in] data_out_format Output format of printed data.
* @param[in] out Output handler.
* @return 0 on success.
*/
static int
print_yang_lib_data(struct ly_ctx *ctx, LYD_FORMAT data_out_format, struct ly_out *out)
{
struct lyd_node *ylib;
if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
YLMSG_E("Getting context info (ietf-yang-library data) failed. If the YANG module is missing or not implemented, "
"use an option to add it internally.");
return 1;
}
lyd_print_all(out, ylib, data_out_format, 0);
lyd_free_all(ylib);
return 0;
}
int
cmd_list_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
(void) posv;
uint32_t idx = 0, has_modules = 0;
const struct lys_module *mod;
if (yo->data_out_format != LYD_UNKNOWN) {
/* ietf-yang-library data are printed in the specified format */
if (print_yang_lib_data(*ctx, yo->data_out_format, yo->out)) {
return 1;
}
return 0;
}
/* iterate schemas in context and provide just the basic info */
ly_print(yo->out, "List of the loaded models:\n");
while ((mod = ly_ctx_get_module_iter(*ctx, &idx))) {
has_modules++;
/* conformance print */
if (mod->implemented) {
ly_print(yo->out, " I");
} else {
ly_print(yo->out, " i");
}
/* module print */
ly_print(yo->out, " %s", mod->name);
if (mod->revision) {
ly_print(yo->out, "@%s", mod->revision);
}
/* submodules print */
if (mod->parsed && mod->parsed->includes) {
uint64_t u = 0;
ly_print(yo->out, " (");
LY_ARRAY_FOR(mod->parsed->includes, u) {
ly_print(yo->out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
if (mod->parsed->includes[u].rev[0]) {
ly_print(yo->out, "@%s", mod->parsed->includes[u].rev);
}
}
ly_print(yo->out, ")");
}
/* finish the line */
ly_print(yo->out, "\n");
}
if (!has_modules) {
ly_print(yo->out, "\t(none)\n");
}
ly_print_flush(yo->out);
return 0;
}

152
tools/lint/cmd_load.c Normal file
View file

@ -0,0 +1,152 @@
/**
* @file cmd_load.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'load' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <assert.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
#include "yl_schema_features.h"
void
cmd_load_help(void)
{
printf("Usage: load [-i] <module-name1>[@<revision>] [<module-name2>[@revision] ...]\n"
" Add a new module of the specified name, yanglint will find\n"
" them in searchpaths. If the <revision> of the module not\n"
" specified, the latest revision available is loaded.\n\n"
" -F FEATURES, --features=FEATURES\n"
" Features to support, default all in all implemented modules.\n"
" <modname>:[<feature>,]*\n"
" -i, --make-implemented\n"
" Make the imported modules \"referenced\" from any loaded\n"
" <schema> module also implemented. If specified a second time,\n"
" all the modules are set implemented.\n"
" -X, --extended-leafref\n"
" Allow usage of deref() XPath function within leafref.\n");
}
int
cmd_load_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc, argc = 0;
int opt, opt_index;
struct option options[] = {
{"features", required_argument, NULL, 'F'},
{"help", no_argument, NULL, 'h'},
{"make-implemented", no_argument, NULL, 'i'},
{"extended-leafref", no_argument, NULL, 'X'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_LOAD].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'F': /* --features */
if (parse_features(optarg, &yo->schema_features)) {
return 1;
}
break;
case 'h':
cmd_load_help();
return 1;
case 'i': /* --make-implemented */
yo_opt_update_make_implemented(yo);
break;
case 'X': /* --extended-leafref */
yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
break;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_load_dep(struct yl_opt *yo, int posc)
{
if (yo->interactive && !posc) {
/* no argument */
cmd_load_help();
return 1;
}
if (!yo->schema_features.count) {
/* no features, enable all of them */
yo->ctx_options |= LY_CTX_ENABLE_IMP_FEATURES;
}
return 0;
}
int
cmd_load_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
const char *all_features[] = {"*", NULL};
char *revision;
const char **features = NULL;
assert(posv);
if (yo->ctx_options) {
ly_ctx_set_options(*ctx, yo->ctx_options);
yo->ctx_options = 0;
}
/* get revision */
revision = strchr(posv, '@');
if (revision) {
revision[0] = '\0';
++revision;
}
/* get features list for this module */
if (!yo->schema_features.count) {
features = all_features;
} else {
get_features(&yo->schema_features, posv, &features);
}
/* load the module */
if (!ly_ctx_load_module(*ctx, posv, revision, features)) {
/* libyang printed the error messages */
return 1;
}
return 0;
}

299
tools/lint/cmd_print.c Normal file
View file

@ -0,0 +1,299 @@
/**
* @file cmd_print.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'print' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <errno.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
void
cmd_print_help(void)
{
printf("Usage: print [-f (yang | yin | tree [-q -P PATH -L LINE_LENGTH ] | info [-q -P PATH])]\n"
" [-o OUTFILE] [<module-name1>[@revision]] ...\n"
" Print a schema module. The <module-name> is not required\n"
" only in case the -P option is specified. For yang, yin and tree\n"
" formats, a submodule can also be printed.\n\n"
" -f FORMAT, --format=FORMAT\n"
" Print the module in the specified FORMAT. If format not\n"
" specified, the 'tree' format is used.\n"
" -L LINE_LENGTH, --tree-line-length=LINE_LENGTH\n"
" The limit of the maximum line length on which the 'tree'\n"
" format will try to be printed.\n"
" -P PATH, --schema-node=PATH\n"
" Print only the specified subtree of the schema.\n"
" The PATH is the XPath subset mentioned in documentation as\n"
" the Path format. The option can be combined with --single-node\n"
" option to print information only about the specified node.\n"
" -q, --single-node\n"
" Supplement to the --schema-node option to print information\n"
" only about a single node specified as PATH argument.\n"
" -o OUTFILE, --output=OUTFILE\n"
" Write the output to OUTFILE instead of stdout.\n");
}
int
cmd_print_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"format", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"tree-line-length", required_argument, NULL, 'L'},
{"output", required_argument, NULL, 'o'},
{"schema-node", required_argument, NULL, 'P'},
{"single-node", no_argument, NULL, 'q'},
{NULL, 0, NULL, 0}
};
yo->schema_out_format = LYS_OUT_TREE;
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_PRINT].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'o': /* --output */
if (yo->out) {
YLMSG_E("Only a single output can be specified.");
return 1;
} else {
if (ly_out_new_filepath(optarg, &yo->out)) {
YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
return 1;
}
}
break;
case 'f': /* --format */
if (yl_opt_update_schema_out_format(optarg, yo)) {
cmd_print_help();
return 1;
}
break;
case 'L': /* --tree-line-length */
yo->line_length = atoi(optarg);
break;
case 'P': /* --schema-node */
yo->schema_node_path = optarg;
break;
case 'q': /* --single-node */
yo->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
break;
case 'h':
cmd_print_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_print_dep(struct yl_opt *yo, int posc)
{
/* file name */
if (yo->interactive && !posc && !yo->schema_node_path) {
YLMSG_E("Missing the name of the module to print.");
return 1;
}
if ((yo->schema_out_format != LYS_OUT_TREE) && yo->line_length) {
YLMSG_W("--tree-line-length take effect only in case of the tree output format.");
}
if (!yo->out) {
if (ly_out_new_file(stdout, &yo->out)) {
YLMSG_E("Could not use stdout to print output.");
}
yo->out_stdout = 1;
}
if (yo->schema_out_format == LYS_OUT_TREE) {
/* print tree from lysc_nodes */
yo->ctx_options |= LY_CTX_SET_PRIV_PARSED;
}
return 0;
}
static LY_ERR
print_submodule(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
{
LY_ERR erc;
const struct lysp_submodule *submodule;
submodule = revision ?
ly_ctx_get_submodule(*ctx, name, revision) :
ly_ctx_get_submodule_latest(*ctx, name);
erc = submodule ?
lys_print_submodule(out, submodule, format, line_length, options) :
LY_ENOTFOUND;
if (!erc) {
return 0;
} else if ((erc == LY_ENOTFOUND) && revision) {
YLMSG_E("No submodule \"%s\" found.", name);
} else {
YLMSG_E("Unable to print submodule %s.", name);
}
return erc;
}
static LY_ERR
print_module(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
{
LY_ERR erc;
struct lys_module *module;
module = revision ?
ly_ctx_get_module(*ctx, name, revision) :
ly_ctx_get_module_latest(*ctx, name);
erc = module ?
lys_print_module(out, module, format, line_length, options) :
LY_ENOTFOUND;
if (!erc) {
return 0;
} else if ((erc == LY_ENOTFOUND) && revision) {
YLMSG_E("No module \"%s\" found.", name);
} else {
YLMSG_E("Unable to print module %s.", name);
}
return erc;
}
static int
cmd_print_module(const char *posv, struct ly_out *out, struct ly_ctx **ctx, LYS_OUTFORMAT format,
size_t line_length, uint32_t options)
{
LY_ERR erc;
char *name = NULL, *revision;
name = strdup(posv);
/* get revision */
revision = strchr(name, '@');
if (revision) {
revision[0] = '\0';
++revision;
}
erc = print_module(out, ctx, name, revision, format, line_length, options);
if (erc == LY_ENOTFOUND) {
erc = print_submodule(out, ctx, name, revision, format, line_length, options);
}
free(name);
return erc;
}
/**
* @brief Print schema node path.
*
* @param[in] ctx Context for libyang.
* @param[in] yo Context for yanglint.
* @return 0 on success.
*/
static int
print_node(struct ly_ctx *ctx, struct yl_opt *yo)
{
const struct lysc_node *node;
uint32_t temp_lo = 0;
if (yo->interactive) {
/* Use the same approach as for completion. */
node = find_schema_path(ctx, yo->schema_node_path);
if (!node) {
YLMSG_E("The requested schema node \"%s\" does not exists.", yo->schema_node_path);
return 1;
}
} else {
/* turn off logging so that the message is not repeated */
ly_temp_log_options(&temp_lo);
/* search operation input */
node = lys_find_path(ctx, NULL, yo->schema_node_path, 0);
if (!node) {
/* restore logging so an error may be displayed */
ly_temp_log_options(NULL);
/* search operation output */
node = lys_find_path(ctx, NULL, yo->schema_node_path, 1);
if (!node) {
YLMSG_E("Invalid schema path.");
return 1;
}
}
}
if (lys_print_node(yo->out, node, yo->schema_out_format, yo->line_length, yo->schema_print_options)) {
YLMSG_E("Unable to print schema node %s.", yo->schema_node_path);
return 1;
}
return 0;
}
int
cmd_print_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
int rc = 0;
if (yo->ctx_options & LY_CTX_SET_PRIV_PARSED) {
/* print tree from lysc_nodes */
ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
}
if (yo->schema_node_path) {
rc = print_node(*ctx, yo);
} else if (!yo->interactive && yo->submodule) {
rc = print_submodule(yo->out, ctx, yo->submodule, NULL, yo->schema_out_format, yo->line_length,
yo->schema_print_options);
} else {
rc = cmd_print_module(posv, yo->out, ctx, yo->schema_out_format, yo->line_length, yo->schema_print_options);
if (!yo->last_one && (yo->schema_out_format == LYS_OUT_TREE)) {
ly_print(yo->out, "\n");
}
}
return rc;
}

View file

@ -0,0 +1,96 @@
/**
* @file cmd_searchpath.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'searchpath' command of the libyang's yanglint tool.
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include "cmd.h"
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
void
cmd_searchpath_help(void)
{
printf("Usage: searchpath [--clear] [<modules-dir-path> ...]\n"
" Set paths of directories where to search for imports and includes\n"
" of the schema modules. Subdirectories are also searched. The current\n"
" working directory and the path of the module being added is used implicitly.\n"
" The 'load' command uses these paths to search even for the schema modules\n"
" to be loaded.\n");
}
int
cmd_searchpath_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"clear", no_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_SEARCHPATH].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'c':
yo->searchdir_unset = 1;
break;
case 'h':
cmd_searchpath_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_searchpath_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
int rc = 0;
if (yo->searchdir_unset) {
ly_ctx_unset_searchdir(*ctx, NULL);
} else if (!yo->searchdir_unset && !posv) {
/* no argument - print the paths */
const char * const *dirs = ly_ctx_get_searchdirs(*ctx);
printf("List of the searchpaths:\n");
for (uint32_t i = 0; dirs[i]; ++i) {
printf(" %s\n", dirs[i]);
}
} else {
rc = ly_ctx_set_searchdir(*ctx, posv);
}
return rc;
}

114
tools/lint/cmd_verb.c Normal file
View file

@ -0,0 +1,114 @@
/**
* @file cmd_verb.c
* @author Adam Piecek <piecek@cesnet.cz>
* @brief 'verb' command of the libyang's yanglint tool.
*
* Copyright (c) 2023-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include "cmd.h"
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <strings.h>
#include "libyang.h"
#include "common.h"
#include "yl_opt.h"
void
cmd_verb_help(void)
{
printf("Usage: verb (error | warning | verbose | debug)\n");
}
int
cmd_verb_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}
while ((opt = getopt_long(argc, yo->argv, commands[CMD_VERB].optstring, options, &opt_index)) != -1) {
if (opt == 'h') {
cmd_verb_help();
return 1;
} else {
YLMSG_E("Unknown option.");
return 1;
}
}
*posv = &yo->argv[optind];
*posc = argc - optind;
return 0;
}
int
cmd_verb_dep(struct yl_opt *yo, int posc)
{
(void) yo;
if (posc > 1) {
YLMSG_E("Only a single verbosity level can be set.");
cmd_verb_help();
return 1;
}
return 0;
}
int
cmd_verb_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
(void) ctx, (void) yo;
if (!posv) {
/* no argument - print current value */
LY_LOG_LEVEL level = ly_log_level(LY_LLERR);
ly_log_level(level);
printf("Current verbosity level: ");
if (level == LY_LLERR) {
printf("error\n");
} else if (level == LY_LLWRN) {
printf("warning\n");
} else if (level == LY_LLVRB) {
printf("verbose\n");
} else if (level == LY_LLDBG) {
printf("debug\n");
}
return 0;
} else {
if (!strcasecmp("error", posv) || !strcmp("0", posv)) {
ly_log_level(LY_LLERR);
} else if (!strcasecmp("warning", posv) || !strcmp("1", posv)) {
ly_log_level(LY_LLWRN);
} else if (!strcasecmp("verbose", posv) || !strcmp("2", posv)) {
ly_log_level(LY_LLVRB);
} else if (!strcasecmp("debug", posv) || !strcmp("3", posv)) {
ly_log_level(LY_LLDBG);
} else {
YLMSG_E("Unknown verbosity \"%s\".", posv);
return 1;
}
}
return 0;
}

301
tools/lint/common.c Normal file
View file

@ -0,0 +1,301 @@
/**
* @file common.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - common functions for both interactive and non-interactive mode.
*
* Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
#include "common.h"
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "compat.h"
#include "libyang.h"
#include "plugins_exts.h"
#include "yl_opt.h"
void
yl_log(ly_bool err, const char *format, ...)
{
char msg[256];
va_list ap;
va_start(ap, format);
vsnprintf(msg, 256, format, ap);
va_end(ap);
fprintf(stderr, "YANGLINT[%c]: %s\n", err ? 'E' : 'W', msg);
}
int
parse_schema_path(const char *path, char **dir, char **module)
{
char *p;
assert(dir);
assert(module);
/* split the path to dirname and basename for further work */
*dir = strdup(path);
/* FIXME: this is broken on Windows */
*module = strrchr(*dir, '/');
if (!(*module)) {
*module = *dir;
*dir = strdup("./");
} else {
*module[0] = '\0'; /* break the dir */
*module = strdup((*module) + 1);
}
/* get the pure module name without suffix or revision part of the filename */
if ((p = strchr(*module, '@'))) {
/* revision */
*p = '\0';
} else if ((p = strrchr(*module, '.'))) {
/* fileformat suffix */
*p = '\0';
}
return 0;
}
int
get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in)
{
struct stat st;
/* check that the filepath exists and is a regular file */
if (stat(filepath, &st) == -1) {
YLMSG_E("Unable to use input filepath (%s) - %s.", filepath, strerror(errno));
return -1;
}
if (!S_ISREG(st.st_mode)) {
YLMSG_E("Provided input file (%s) is not a regular file.", filepath);
return -1;
}
if (get_format(filepath, format_schema, format_data)) {
return -1;
}
if (in && ly_in_new_filepath(filepath, 0, in)) {
YLMSG_E("Unable to process input file.");
return -1;
}
return 0;
}
LYS_INFORMAT
get_schema_format(const char *filename)
{
char *ptr;
if ((ptr = strrchr(filename, '.')) != NULL) {
++ptr;
if (!strcmp(ptr, "yang")) {
return LYS_IN_YANG;
} else if (!strcmp(ptr, "yin")) {
return LYS_IN_YIN;
} else {
return LYS_IN_UNKNOWN;
}
} else {
return LYS_IN_UNKNOWN;
}
}
LYD_FORMAT
get_data_format(const char *filename)
{
char *ptr;
if ((ptr = strrchr(filename, '.')) != NULL) {
++ptr;
if (!strcmp(ptr, "xml")) {
return LYD_XML;
} else if (!strcmp(ptr, "json")) {
return LYD_JSON;
} else if (!strcmp(ptr, "lyb")) {
return LYD_LYB;
} else {
return LYD_UNKNOWN;
}
} else {
return LYD_UNKNOWN;
}
}
int
get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form)
{
LYS_INFORMAT schema;
LYD_FORMAT data;
schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form;
data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form;
if (!schema) {
schema = get_schema_format(filepath);
}
if (!data) {
data = get_data_format(filepath);
}
if (!schema && !data) {
YLMSG_E("Input schema format for %s file not recognized.", filepath);
return -1;
} else if (!data && !schema) {
YLMSG_E("Input data format for %s file not recognized.", filepath);
return -1;
}
assert(schema || data);
if (schema_form) {
*schema_form = schema;
}
if (data_form) {
*data_form = data;
}
return 0;
}
const struct lysc_node *
find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
{
const char *end, *module_name_end;
char *module_name = NULL;
const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
const struct lys_module *module;
size_t node_name_len;
ly_bool found_exact_match = 0;
/* iterate over each '/' in the path */
while (schema_path) {
/* example: schema_path = /listen/endpoint
* end == NULL for endpoint, end exists for listen */
end = strchr(schema_path + 1, '/');
if (end) {
node_name_len = end - schema_path - 1;
} else {
node_name_len = strlen(schema_path + 1);
}
/* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
module_name_end = strchr(schema_path, ':');
if (module_name_end && (!end || (module_name_end < end))) {
/* only get module's name, if it is in the current scope */
free(module_name);
/* - 1 because module_name_end points to ':' */
module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
if (!module_name) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
parent_node = NULL;
goto cleanup;
}
/* move the pointer to the beginning of the node's name - 1 */
schema_path = module_name_end;
/* recalculate the length of the node's name, because the module prefix mustn't be compared later */
if (module_name_end < end) {
node_name_len = end - module_name_end - 1;
} else if (!end) {
node_name_len = strlen(module_name_end + 1);
}
}
module = ly_ctx_get_module_implemented(ctx, module_name);
if (!module) {
/* unknown module name */
parent_node = NULL;
goto cleanup;
}
/* iterate over the node's siblings / module's top level containers */
while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
/* check if the whole node's name matches and it's not just a common prefix */
parent_node = node;
break;
} else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
/* do the same here, however if there is no exact match, use the last node with the same prefix */
if (strlen(node->name) == node_name_len) {
parent_node = node;
found_exact_match = 1;
break;
} else {
parent_node_tmp = node;
}
}
}
if (!end && !found_exact_match) {
/* no exact match */
parent_node = parent_node_tmp;
}
found_exact_match = 0;
/* next iter */
schema_path = strchr(schema_path + 1, '/');
}
cleanup:
free(module_name);
return parent_node;
}
LY_ERR
ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
{
struct ly_ctx *ctx;
struct lyd_node *data = NULL;
ctx = ext->module->ctx;
if (user_data) {
lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
}
*ext_data = data;
*ext_data_free = 1;
return LY_SUCCESS;
}
LY_ERR
searchpath_strcat(char **searchpaths, const char *path)
{
uint64_t len;
char *new;
if (!(*searchpaths)) {
*searchpaths = strdup(path);
return LY_SUCCESS;
}
len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
new = realloc(*searchpaths, sizeof(char) * len + 1);
if (!new) {
return LY_EMEM;
}
strcat(new, PATH_SEPARATOR);
strcat(new, path);
*searchpaths = new;
return LY_SUCCESS;
}

158
tools/lint/common.h Normal file
View file

@ -0,0 +1,158 @@
/**
* @file common.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - common functions and definitions for both interactive and non-interactive mode.
*
* Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef COMMON_H_
#define COMMON_H_
#include <stdint.h>
#include <stdio.h>
#include "libyang.h"
#define PROMPT "> "
/**
* @brief Default context creation options.
*/
#define YL_DEFAULT_CTX_OPTIONS LY_CTX_NO_YANGLIBRARY
/**
* @brief Default data parsing flags.
*/
#define YL_DEFAULT_DATA_PARSE_OPTIONS LYD_PARSE_STRICT
/**
* @brief Default data validation flags.
*/
#define YL_DEFAULT_DATA_VALIDATE_OPTIONS LYD_VALIDATE_MULTI_ERROR
/**
* @brief log error message
*/
#define YLMSG_E(...) \
yl_log(1, __VA_ARGS__);
/**
* @brief log warning message
*/
#define YLMSG_W(...) \
yl_log(0, __VA_ARGS__);
#ifndef _WIN32
# define PATH_SEPARATOR ":"
#else
# define PATH_SEPARATOR ";"
#endif
struct cmdline_file;
/**
* @brief Log a yanglint message.
*
* @param[in] err Whether the message is an error or a warning.
* @param[in] format Message format.
* @param[in] ... Format arguments.
*/
void yl_log(ly_bool err, const char *format, ...);
/**
* @brief Parse path of a schema module file into the directory and module name.
*
* @param[in] path Schema module file path to be parsed.
* @param[out] dir Pointer to the directory path where the file resides. Caller is expected to free the returned string.
* @param[out] module Pointer to the name of the module (without file suffixes or revision information) specified by the
* @p path. Caller is expected to free the returned string.
* @return 0 on success
* @return -1 on error
*/
int parse_schema_path(const char *path, char **dir, char **module);
/**
* @brief Get input handler for the specified path.
*
* Using the @p format_schema and @p format_data the type of the file can be limited (by providing NULL) or it can be
* got known if both types are possible.
*
* @param[in] filepath Path of the file to open.
* @param[out] format_schema Format of the schema detected from the file name. If NULL specified, the schema formats are
* prohibited and such files are refused.
* @param[out] format_data Format of the data detected from the file name. If NULL specified, the data formats are
* prohibited and such files are refused.
* @param[out] in Created input handler referring the file behind the @p filepath. Can be NULL.
* @return 0 on success.
* @return -1 on failure.
*/
int get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in);
/**
* @brief Get schema format of the @p filename's content according to the @p filename's suffix.
*
* @param[in] filename Name of the file to examine.
* @return Detected schema input format.
*/
LYS_INFORMAT get_schema_format(const char *filename);
/**
* @brief Get data format of the @p filename's content according to the @p filename's suffix.
*
* @param[in] filename Name of the file to examine.
* @return Detected data input format.
*/
LYD_FORMAT get_data_format(const char *filename);
/**
* @brief Get format of the @p filename's content according to the @p filename's suffix.
*
* Either the @p schema or @p data parameter is set.
*
* @param[in] filepath Name of the file to examine.
* @param[out] schema_form Pointer to a variable to store the input schema format.
* @param[out] data_form Pointer to a variable to store the expected input data format.
* @return zero in case a format was successfully detected.
* @return nonzero in case it is not possible to get valid format from the @p filename.
*/
int get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form);
/**
* @brief Get the node specified by the path.
*
* @param[in] ctx libyang context with schema.
* @param[in] schema_path Path to the wanted node.
* @return Pointer to the schema node specified by the path on success, NULL otherwise.
*/
const struct lysc_node *find_schema_path(const struct ly_ctx *ctx, const char *schema_path);
/**
* @brief General callback providing run-time extension instance data.
*
* @param[in] ext Compiled extension instance.
* @param[in] user_data User-supplied callback data.
* @param[out] ext_data Provided extension instance data.
* @param[out] ext_data_free Whether the extension instance should free @p ext_data or not.
* @return LY_ERR value.
*/
LY_ERR ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free);
/**
* @brief Concatenation of paths into one string.
*
* @param[in,out] searchpaths Collection of paths in the single string. Paths are delimited by colon ":"
* (on Windows, used semicolon ";" instead).
* @param[in] path Path to add.
* @return LY_ERR value.
*/
LY_ERR searchpath_strcat(char **searchpaths, const char *path);
#endif /* COMMON_H_ */

508
tools/lint/completion.c Normal file
View file

@ -0,0 +1,508 @@
/**
* @file completion.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libyang's yanglint tool auto completion
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L /* strdup */
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libyang.h"
#include "cmd.h"
#include "common.h"
#include "compat.h"
#include "linenoise/linenoise.h"
/* from the main.c */
extern struct ly_ctx *ctx;
/**
* @brief Add a match to the completions array.
*
* @param[in] match Match to be added.
* @param[in,out] matches Matches provided to the user as a completion hint.
* @param[in,out] match_count Number of matches.
*/
static void
cmd_completion_add_match(const char *match, char ***matches, unsigned int *match_count)
{
void *p;
p = realloc(*matches, (*match_count + 1) * sizeof **matches);
if (!p) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
return;
}
*matches = p;
(*matches)[*match_count] = strdup(match);
if (!((*matches)[*match_count])) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
return;
}
++(*match_count);
}
/**
* @brief Provides completion for command names.
*
* @param[in] hint User input.
* @param[out] matches Matches provided to the user as a completion hint.
* @param[out] match_count Number of matches.
*/
static void
get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
{
int i;
*match_count = 0;
*matches = NULL;
for (i = 0; commands[i].name; i++) {
if (!strncmp(hint, commands[i].name, strlen(hint))) {
cmd_completion_add_match(commands[i].name, matches, match_count);
}
}
}
/**
* @brief Provides completion for arguments.
*
* @param[in] hint User input.
* @param[in] args Array of all possible arguments. The last element must be NULL.
* @param[out] matches Matches provided to the user as a completion hint.
* @param[out] match_count Number of matches.
*/
static void
get_arg_completion(const char *hint, const char **args, char ***matches, unsigned int *match_count)
{
int i;
*match_count = 0;
*matches = NULL;
for (i = 0; args[i]; i++) {
if (!strncmp(hint, args[i], strlen(hint))) {
cmd_completion_add_match(args[i], matches, match_count);
}
}
if (*match_count == 0) {
for (i = 0; args[i]; i++) {
cmd_completion_add_match(args[i], matches, match_count);
}
}
}
/**
* @brief Provides completion for module names.
*
* @param[in] hint User input.
* @param[out] matches Matches provided to the user as a completion hint.
* @param[out] match_count Number of matches.
*/
static void
get_model_completion(const char *hint, char ***matches, unsigned int *match_count)
{
LY_ARRAY_COUNT_TYPE u;
uint32_t idx = 0;
const struct lys_module *module;
*match_count = 0;
*matches = NULL;
while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
if (!strncmp(hint, module->name, strlen(hint))) {
cmd_completion_add_match(module->name, matches, match_count);
}
LY_ARRAY_FOR(module->parsed->includes, u) {
if (!strncmp(hint, module->parsed->includes[u].submodule->name, strlen(hint))) {
cmd_completion_add_match(module->parsed->includes[u].submodule->name, matches, match_count);
}
}
}
}
/**
* @brief Add all child nodes of a single node to the completion hint.
*
* @param[in] parent Node of which children will be added to the hint.
* @param[out] matches Matches provided to the user as a completion hint.
* @param[out] match_count Number of matches.
*/
static void
single_hint_add_children(const struct lysc_node *parent, char ***matches, unsigned int *match_count)
{
const struct lysc_node *node = NULL;
char *match;
if (!parent) {
return;
}
while ((node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
match = lysc_path(node, LYSC_PATH_LOG, NULL, 0);
cmd_completion_add_match(match, matches, match_count);
free(match);
}
}
/**
* @brief Add module and/or node's children names to the hint.
*
* @param[in] module Compiled schema module.
* @param[in] parent Parent node of which children are potential matches.
* @param[in] hint_node_name Node name contained within the hint specified by user.
* @param[in,out] matches Matches provided to the user as a completion hint.
* @param[in,out] match_count Number of matches.
* @param[out] last_node Last processed node.
*/
static void
add_all_children_nodes(const struct lysc_module *module, const struct lysc_node *parent,
const char *hint_node_name, char ***matches, unsigned int *match_count, const struct lysc_node **last_node)
{
const struct lysc_node *node;
char *match, *node_name = NULL;
*last_node = NULL;
if (!parent && !module) {
return;
}
node = NULL;
while ((node = lys_getnext(node, parent, module, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
if (parent && (node->module != parent->module)) {
/* augmented node */
if (asprintf(&node_name, "%s:%s", node->module->name, node->name) == -1) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
break;
}
} else {
node_name = strdup(node->name);
if (!node_name) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
break;
}
}
if (!hint_node_name || !strncmp(hint_node_name, node_name, strlen(hint_node_name))) {
/* adding just module names + their top level node(s) to the hint */
*last_node = node;
match = lysc_path(node, LYSC_PATH_LOG, NULL, 0);
cmd_completion_add_match(match, matches, match_count);
free(match);
}
free(node_name);
}
}
/**
* @brief Provides completion for schemas.
*
* @param[in] hint User input.
* @param[out] matches Matches provided to the user as a completion hint.
* @param[out] match_count Number of matches.
*/
static void
get_schema_completion(const char *hint, char ***matches, unsigned int *match_count)
{
const struct lys_module *module;
uint32_t idx;
const char *start;
char *end, *module_name = NULL, *path = NULL;
const struct lysc_node *parent, *last_node;
int rc = 0;
size_t len;
*match_count = 0;
*matches = NULL;
if (strlen(hint)) {
if (hint[0] != '/') {
return;
}
start = hint + 1;
} else {
start = hint;
}
end = strchr(start, ':');
if (!end) {
/* no module name */
len = strlen(start);
/* go through all the modules */
idx = 0;
while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
if (!module->implemented) {
continue;
}
if (!len || !strncmp(start, module->name, len)) {
/* add all their (matching) top level nodes */
add_all_children_nodes(module->compiled, NULL, NULL, matches, match_count, &last_node);
}
}
} else {
/* module name known */
module_name = strndup(start, end - start);
if (!module_name) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
rc = 1;
goto cleanup;
}
module = ly_ctx_get_module_implemented(ctx, module_name);
if (!module) {
goto cleanup;
}
/* find the last '/', if it is at the beginning of the hint, only path up to the top level node is known,
* else the name of the last node starts after the found '/' */
start = strrchr(hint, '/');
if (!start) {
goto cleanup;
}
if (start == hint) {
/* only the (incomplete) top level node path, add all (matching) top level nodes */
add_all_children_nodes(module->compiled, NULL, end + 1, matches, match_count, &last_node);
goto cleanup;
}
/* get rid of stuff after the last '/' to obtain the parent node */
path = strndup(hint, start - hint);
if (!path) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
rc = 1;
goto cleanup;
}
/* get the last parent in the hint (it may not exist) */
parent = find_schema_path(ctx, path);
/* add all (matching) child nodes of the parent */
add_all_children_nodes(NULL, parent, start + 1, matches, match_count, &last_node);
}
cleanup:
if (!rc && (*match_count == 1)) {
/* to avoid a single hint (space at the end), add all children as hints */
single_hint_add_children(last_node, matches, match_count);
}
free(path);
free(module_name);
}
/**
* @brief Get all possible argument hints for option.
*
* @param[in] hint User input.
* @param[out] matches Matches provided to the user as a completion hint.
* @param[out] match_count Number of matches.
*/
static void
get_print_format_arg(const char *hint, char ***matches, unsigned int *match_count)
{
const char *args[] = {"yang", "yin", "tree", "info", NULL};
get_arg_completion(hint, args, matches, match_count);
}
/**
* @copydoc get_print_format_arg
*/
static void
get_data_type_arg(const char *hint, char ***matches, unsigned int *match_count)
{
const char *args[] = {"data", "config", "get", "getconfig", "edit", "rpc", "reply", "notif", NULL};
get_arg_completion(hint, args, matches, match_count);
}
/**
* @copydoc get_print_format_arg
*/
static void
get_data_in_format_arg(const char *hint, char ***matches, unsigned int *match_count)
{
const char *args[] = {"xml", "json", "lyb", NULL};
get_arg_completion(hint, args, matches, match_count);
}
/**
* @copydoc get_print_format_arg
*/
static void
get_data_default_arg(const char *hint, char ***matches, unsigned int *match_count)
{
const char *args[] = {"all", "all-tagged", "trim", "implicit-tagged", NULL};
get_arg_completion(hint, args, matches, match_count);
}
/**
* @copydoc get_print_format_arg
*/
static void
get_list_format_arg(const char *hint, char ***matches, unsigned int *match_count)
{
const char *args[] = {"xml", "json", NULL};
get_arg_completion(hint, args, matches, match_count);
}
/**
* @copydoc get_print_format_arg
*/
static void
get_verb_arg(const char *hint, char ***matches, unsigned int *match_count)
{
const char *args[] = {"error", "warning", "verbose", "debug", NULL};
get_arg_completion(hint, args, matches, match_count);
}
/**
* @copydoc get_print_format_arg
*/
static void
get_debug_arg(const char *hint, char ***matches, unsigned int *match_count)
{
const char *args[] = {"dict", "xpath", "dep-sets", NULL};
get_arg_completion(hint, args, matches, match_count);
}
/**
* @brief Get the string before the hint, which autocompletion is for.
*
* @param[in] buf Complete user input.
* @param[in] hint Hint part of the user input.
* @return Pointer to the last string.
*/
static const char *
get_last_str(const char *buf, const char *hint)
{
const char *ptr;
if (buf == hint) {
return buf;
}
ptr = hint - 1;
while (ptr[0] == ' ') {
--ptr;
if (buf == ptr) {
return buf;
}
}
while (ptr[-1] != ' ') {
--ptr;
if (buf == ptr) {
return buf;
}
}
return ptr;
}
/* callback */
void
complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
{
struct autocomplete {
enum COMMAND_INDEX ci; /**< command index to global variable 'commands' */
const char *opt; /**< optional option */
void (*ln_cb)(const char *, const char *, linenoiseCompletions *); /**< linenoise callback to call */
void (*yl_cb)(const char *, char ***, unsigned int *); /**< yanglint callback to call */
} ac[] = {
{CMD_ADD, NULL, linenoisePathCompletion, NULL},
{CMD_PRINT, "-f", NULL, get_print_format_arg},
{CMD_PRINT, "-P", NULL, get_schema_completion},
{CMD_PRINT, "-o", linenoisePathCompletion, NULL},
{CMD_PRINT, NULL, NULL, get_model_completion},
{CMD_SEARCHPATH, NULL, linenoisePathCompletion, NULL},
{CMD_EXTDATA, NULL, linenoisePathCompletion, NULL},
{CMD_CLEAR, "-Y", linenoisePathCompletion, NULL},
{CMD_DATA, "-t", NULL, get_data_type_arg},
{CMD_DATA, "-O", linenoisePathCompletion, NULL},
{CMD_DATA, "-R", linenoisePathCompletion, NULL},
{CMD_DATA, "-f", NULL, get_data_in_format_arg},
{CMD_DATA, "-F", NULL, get_data_in_format_arg},
{CMD_DATA, "-d", NULL, get_data_default_arg},
{CMD_DATA, "-o", linenoisePathCompletion, NULL},
{CMD_DATA, NULL, linenoisePathCompletion, NULL},
{CMD_LIST, NULL, NULL, get_list_format_arg},
{CMD_FEATURE, NULL, NULL, get_model_completion},
{CMD_VERB, NULL, NULL, get_verb_arg},
{CMD_DEBUG, NULL, NULL, get_debug_arg},
};
size_t name_len;
const char *last, *name, *getoptstr;
char opt[3] = {'\0', ':', '\0'};
char **matches = NULL;
unsigned int match_count = 0, i;
if (buf == hint) {
/* command autocomplete */
get_cmd_completion(hint, &matches, &match_count);
} else {
for (i = 0; i < (sizeof ac / sizeof *ac); ++i) {
/* Find the right command. */
name = commands[ac[i].ci].name;
name_len = strlen(name);
if (strncmp(buf, name, name_len) || (buf[name_len] != ' ')) {
/* not this command */
continue;
}
/* Select based on the right option. */
last = get_last_str(buf, hint);
opt[0] = (last[0] == '-') && last[1] ? last[1] : '\0';
getoptstr = commands[ac[i].ci].optstring;
if (!ac[i].opt && opt[0] && strstr(getoptstr, opt)) {
/* completion for the argument must be defined */
continue;
} else if (ac[i].opt && opt[0] && strncmp(ac[i].opt, last, strlen(ac[i].opt))) {
/* completion for (another) option */
continue;
} else if (ac[i].opt && !opt[0]) {
/* completion is defined for option */
continue;
}
/* callback */
if (ac[i].ln_cb) {
ac[i].ln_cb(buf, hint, lc);
} else {
ac[i].yl_cb(hint, &matches, &match_count);
}
break;
}
}
/* transform matches into autocompletion, if needed */
for (i = 0; i < match_count; ++i) {
linenoiseAddCompletion(lc, matches[i]);
free(matches[i]);
}
free(matches);
}

25
tools/lint/completion.h Normal file
View file

@ -0,0 +1,25 @@
/**
* @file completion.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libyang's yanglint tool auto completion header
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef COMPLETION_H_
#define COMPLETION_H_
#include "./linenoise/linenoise.h"
/**
* @brief Command line completion callback.
*/
void complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc);
#endif /* COMPLETION_H_ */

125
tools/lint/configuration.c Normal file
View file

@ -0,0 +1,125 @@
/**
* @file configuration.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief yanglint configuration
*
* Copyright (c) 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include "configuration.h"
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "linenoise/linenoise.h"
#include "common.h"
/* Yanglint home (appended to ~/) */
#define YL_DIR ".yanglint"
char *
get_yanglint_dir(void)
{
int ret;
struct passwd *pw;
char *user_home, *yl_dir;
if (!(pw = getpwuid(getuid()))) {
YLMSG_E("Determining home directory failed (%s).", strerror(errno));
return NULL;
}
user_home = pw->pw_dir;
yl_dir = malloc(strlen(user_home) + 1 + strlen(YL_DIR) + 1);
if (!yl_dir) {
YLMSG_E("Memory allocation failed (%s).", strerror(errno));
return NULL;
}
sprintf(yl_dir, "%s/%s", user_home, YL_DIR);
ret = access(yl_dir, R_OK | X_OK);
if (ret == -1) {
if (errno == ENOENT) {
/* directory does not exist */
YLMSG_W("Configuration directory \"%s\" does not exist, creating it.", yl_dir);
if (mkdir(yl_dir, 00700)) {
if (errno != EEXIST) {
/* parallel execution, yay */
YLMSG_E("Configuration directory \"%s\" cannot be created (%s).", yl_dir, strerror(errno));
free(yl_dir);
return NULL;
}
}
} else {
YLMSG_E("Configuration directory \"%s\" exists but cannot be accessed (%s).", yl_dir, strerror(errno));
free(yl_dir);
return NULL;
}
}
return yl_dir;
}
void
load_config(void)
{
char *yl_dir, *history_file;
if ((yl_dir = get_yanglint_dir()) == NULL) {
return;
}
history_file = malloc(strlen(yl_dir) + 9);
if (!history_file) {
YLMSG_E("Memory allocation failed (%s).", strerror(errno));
free(yl_dir);
return;
}
sprintf(history_file, "%s/history", yl_dir);
if (access(history_file, F_OK) && (errno == ENOENT)) {
YLMSG_W("No saved history.");
} else if (linenoiseHistoryLoad(history_file)) {
YLMSG_E("Failed to load history.");
}
free(history_file);
free(yl_dir);
}
void
store_config(void)
{
char *yl_dir, *history_file;
if ((yl_dir = get_yanglint_dir()) == NULL) {
return;
}
history_file = malloc(strlen(yl_dir) + 9);
if (!history_file) {
YLMSG_E("Memory allocation failed (%s).", strerror(errno));
free(yl_dir);
return;
}
sprintf(history_file, "%s/history", yl_dir);
if (linenoiseHistorySave(history_file)) {
YLMSG_E("Failed to save history.");
}
free(history_file);
free(yl_dir);
}

View file

@ -0,0 +1,36 @@
/**
* @file configuration.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief yanglint configuration header
*
* Copyright (c) 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
/**
* @brief Finds the current user's yanglint dir
* @return NULL on failure, dynamically allocated yanglint dir path
* otherwise
*/
char *get_yanglint_dir(void);
/**
* @brief Checks all the relevant files and directories creating any
* that are missing, sets the saved configuration (currently only history)
*/
void load_config(void);
/**
* @brief Saves the current configuration (currently only history)
*/
void store_config(void);
#endif /* CONFIGURATION_H_ */

View file

@ -0,0 +1,536 @@
# YANGLINT - Interactive Mode Examples
This text provides several use-case of the `yanglint(1)` interactive
mode. For basic information about the `yanglint(1)` usage, please see
the man page.
The examples are supposed to be went through one by one. Some of the examples
suppose the specific schemas loaded in some of the previous example is still
loaded. If an addition work is need, the *preparation* part in the example
provides information what to do.
To show all available command of the `yanglint(1)`, use the `help` command:
```
> help
Available commands:
help Display commands description
add Add a new module from a specific file
load Load a new schema from the searchdirs
print Print a module
data Load, validate and optionally print instance data
list List all the loaded modules
feature Print all features of module(s) with their state
searchpath Print/set the search path(s) for schemas
clear Clear the context - remove all the loaded modules
verb Change verbosity
debug Display specific debug message groups
quit Quit the program
? Display commands description
exit Quit the program
```
To show the information about the specific command, use the `help` command in
combination with the command name you are interested in:
```
> help searchpath
Usage: searchpath [--clear] [<modules-dir-path> ...]
Set paths of directories where to search for imports and includes
of the schema modules. Subdirectories are also searched. The current
working directory and the path of the module being added is used implicitly.
The 'load' command uses these paths to search even for the schema modules
to be loaded.
```
The input files referred in this document are available together with this
document.
## Duplicit Data Model
Let's have two data models [module1.yang](./module1.yang)
and [module1b.yang](./module1b.yang).
They differ in the module name but their namespaces are the same.
Preparation:
```
> clear
> add module1.yang
> list
```
Output:
```
List of the loaded models:
i ietf-yang-metadata@2016-08-05
I yang@2022-06-16
i ietf-inet-types@2013-07-15
i ietf-yang-types@2013-07-15
I ietf-yang-schema-mount@2019-01-14
I module1
```
Command and its output:
```
> add module1b.yang
libyang[0]: Two different modules ("module1" and "module1b") have the same namespace "urn:yanglint:module".
libyang[0]: Parsing module "module1b" failed.
```
## Yang Data Model Validation
**YANG/YIN syntax**
`module2.yin` contains a syntax error.
There is a bad syntax of the `type` statement in YIN file.
```
<type value="string"/>
```
instead of
```
<type name="string"/>
```
Preparation:
```
> clear
```
Command and its output:
```
> add module2.yin
libyang[0]: Unexpected attribute "value" of "type" element. (path: Line number 8.)
libyang[0]: Parsing module "module2" failed.
```
Similarly, there is a typo in `module2.yang`.
**XPath errors**
`libyang` and `yanglint(1)` is able to detect also errors in XPath expressions.
In `module3.yang` the `must` expression refers to the node which does not exists.
Preparation:
```
> clear
```
Command and its output:
```
> add module3.yang
libyang[1]: Schema node "a" for parent "/module3:c" not found; in expr "../c/a" with context node "/module3:m".
```
Note that libyang prints only a warning in this case because it is not
specified that XPath expressions must refer to existing nodes.
## Data Validation
Preparation:
```
> clear
> add ietf-netconf-acm.yang
```
**Unknown data**
By default, yanglint ignores unknown data and no error is printed (you can
compare real content of the `datastore.xml` file and what yanglint prints
in the following command if you add `-f xml` option).
Command and its output:
```
> data -t config datastore.xml
```
We use option `-t` to specify type of the data in `datastore.xml`. By the
`config` value we declare that the input file contains all the configuration
data (with at least all the mandatory nodes as required by the loaded schemas),
but without the status data. More examples of different data types will follow.
Command and its output:
```
> data -t config datastore.xml
libyang[0]: No module with namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces" in the context. (path: Line number 20.)
YANGLINT[E]: Failed to parse input data file "datastore.xml".
```
Note that in case of working with complete datastore including the status data
(no `-t` option is specified), `yanglint(1)` has to add status data from its
internal `ietf-yang-library` module.
**RPC and RPC-reply**
It is possible to validate RPCs and their replies as well.
Peparation:
```
> clear
> add module4.yang
```
Command and its output:
```
> data -t rpc rpc.xml
```
Reply to this RPC can be validated too, but it must be nested in the original
RPC element.
Command and its output:
```
> data -t reply ../tools/lint/examples/rpc-reply.xml
```
**action and action-reply**
Actions are validated the same way as RPCs except you need to be careful
about the input file structure. No NETCONF-specific envelopes are expected.
Preparation
```
> clear
> add module4.yang
```
Command and its output:
```
> data -t rpc action.xml
```
Command and its output:
```
> data -t rpc action-reply.xml action.xml
```
**notification**
Both top-level and nested notification can be validated.
Preparation
```
> clear
> add module4.yang
```
Command and its output:
```
> data -t notif notification.xml
```
Command and its output:
```
> data -t notif nested-notification.xml
```
**Multiple top-level elements in a single document**
As a feature and in conflict with the XML definition, `yanglint(1)` (and libyang)
is able to read XML files with multiple top-level elements. Such documents
are not well-formed according to the XML spec, but it fits to how the YANG
interconnects data trees (defined as top-level elements of a single schema
or by multiple schemas).
Preparation:
```
> clear
> add ietf-netconf-acm.yang
> add ietf-interfaces.yang
> add ietf-ip.yang
> add iana-if-type.yang
```
Command and its output:
```
> data -t config datastore.xml
```
**Different data content types**
Since NETCONF requires the data described by YANG to be used in different
situations (e.g. as <edit-config data>, result of the <get> with status data
included or as a result of the <get-config> without the status data and
possibly filtered, so without specified subtrees), it must be possible to
specify which kind of data is going to be parsed. In `yanglint(1)`, this is done
via `-t` option. The list of supported modes can be displayed by the `-h`
option given to the `data` command. In general, the `auto` value lets the
`yanglint(1)` to recognize the data type automatically by the additional top-level
elements added to the parsed data. This is the same way as `pyang(1)` uses. Note,
that the automatic data type recognition is available only for the XML input.
**Malformed XML data**
Command and its output:
```
> data -t edit config-missing-key.xml
libyang[0]: Node "nam" not found as a child of "group" node. (path: Schema location "/ietf-netconf-acm:nacm/groups/group", data location "/ietf-netconf-acm:group", line number 19.)
YANGLINT[E]: Failed to parse input data file "config-missing-key.xml".
```
**State information in edit-config XML**
Command and its output:
```
> data -t edit config-unknown-element.xml
libyang[0]: Unexpected data state node "denied-operations" found. (path: Schema location "/ietf-netconf-acm:nacm/denied-operations", data location "/ietf-netconf-acm:nacm", line number 24.)
YANGLINT[E]: Failed to parse input data file "config-unknown-element.xml".
```
**Missing required element in NETCONF data**
Command and its output:
```
> data data-missing-key.xml
libyang[0]: List instance is missing its key "name". (path: Schema location "/ietf-netconf-acm:nacm/rule-list/rule", data location "/ietf-netconf-acm:rule", line number 10.)
YANGLINT[E]: Failed to parse input data file "data-missing-key.xml".
```
**Malformed XML**
Command and its output:
```
> data data-malformed-xml.xml
libyang[0]: Node "nam" not found as a child of "rule" node. (path: Schema location "/ietf-netconf-acm:nacm/rule-list/rule", data location "/ietf-netconf-acm:rule", line number 8.)
YANGLINT[E]: Failed to parse input data file "data-malformed-xml.xml".
```
Command and its output:
```
> data data-malformed-xml2.xml
libyang[0]: Child element "module-name" inside a terminal node "name" found. (path: Schema location "/ietf-netconf-acm:nacm/rule-list/rule/name", data location "/ietf-netconf-acm:name", line number 7.)
YANGLINT[E]: Failed to parse input data file "data-malformed-xml2.xml".
```
**Bad value**
Command and its output:
```
> data data-out-of-range-value.xml
libyang[0]: Value "-1" is out of type uint32 min/max bounds. (path: Schema location "/ietf-netconf-acm:nacm/denied-operations", data location "/ietf-netconf-acm:nacm", line number 24.)
YANGLINT[E]: Failed to parse input data file "data-out-of-range-value.xml".
```
## Validation of "when" Statement in Data
Preparation:
```
> clear
> add ietf-netconf-acm-when.yang
```
**`When` condition is not satisfied since `denied-operation = 0`**
Command and its output:
```
> data data-acm.xml
libyang[0]: When condition "../denied-operations > 0" not satisfied. (path: Schema location "/ietf-netconf-acm-when:nacm/denied-data-writes", data location "/ietf-netconf-acm-when:nacm/denied-data-writes".)
YANGLINT[E]: Failed to parse input data file "data-acm.xml".
```
## Printing a Data Model
Preparation:
```
> clear
> add ietf-netconf-acm.yang
```
**Print a `pyang`-style tree**
Command and its output:
```
> print ietf-netconf-acm
module: ietf-netconf-acm
+--rw nacm
+--rw enable-nacm? boolean
+--rw read-default? action-type
+--rw write-default? action-type
+--rw exec-default? action-type
+--rw enable-external-groups? boolean
+--ro denied-operations yang:zero-based-counter32
+--ro denied-data-writes yang:zero-based-counter32
+--ro denied-notifications yang:zero-based-counter32
+--rw groups
| +--rw group* [name]
| +--rw name group-name-type
| +--rw user-name* user-name-type
+--rw rule-list* [name]
+--rw name string
+--rw group* union
+--rw rule* [name]
+--rw name string
+--rw module-name? union
+--rw (rule-type)?
| +--:(protocol-operation)
| | +--rw rpc-name? union
| +--:(notification)
| | +--rw notification-name? union
| +--:(data-node)
| +--rw path node-instance-identifier
+--rw access-operations? union
+--rw action action-type
+--rw comment? string
```
**Print information about specific model part**
Command and its output:
```
> print -f info -P /ietf-netconf-acm:nacm/ietf-netconf-acm:enable-nacm ietf-netconf-acm
leaf enable-nacm {
ietf-netconf-acm:default-deny-all;
type boolean;
default "true";
config true;
status current;
description
"Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.";
}
```
## Usage of `feature` in Yang
Preparation:
```
> clear
> add ietf-interfaces.yang
> add ietf-ip.yang -F ietf-ip:*
> add iana-if-type.yang
```
Note: This example also shows `JSON` output of the command.
Command and its output:
```
> feature ietf-ip
ietf-ip features:
ipv4-non-contiguous-netmasks (on)
ipv6-privacy-autoconf (on)
> data -f json -t config data-ip.xml
{
"ietf-interfaces:interfaces": {
"interface": [
{
"name": "eth0",
"description": "Wire Connection",
"type": "iana-if-type:ethernetCsmacd",
"enabled": true,
"ietf-ip:ipv4": {
"address": [
{
"ip": "192.168.1.15",
"netmask": "255.255.255.0"
},
{
"ip": "192.168.1.10",
"netmask": "255.255.255.0"
}
]
}
}
]
}
}
```
## YANG modules with the Schema Mount extension
In these examples the non-interactive `yanglint` is used to simplify creating the context, a `yang-library` data file is
used. The working directory is `libyang/tools/lint/examples` and *libyang* must be installed.
**Print tree output of a model with Schema Mount**
Command and its output:
```
$ yanglint -f tree -p . -Y sm-context-main.xml -x sm-context-extension.xml sm-main.yang
module: sm-main
+--mp root* [node]
| +--rw node string
+--mp root2
+--rw root3
+--mp my-list* [name]
+--rw things/* [name]
| +--rw name -> /if:interfaces/if:interface/if:name
| +--rw attribute? uint32
+--rw not-compiled/
| +--rw first? string
| +--rw second? string
+--rw interfaces@
| +--rw interface* [name]
| +--rw name string
| +--rw type identityref
+--rw name string
```
**Validating and printing mounted data**
Command and its output:
```
$ yanglint -f json -t config -p . -Y sm-context-main.xml -x sm-context-extension.xml sm-data.xml
{
"ietf-interfaces:interfaces": {
"interface": [
{
"name": "eth0",
"type": "iana-if-type:ethernetCsmacd"
},
{
"name": "eth1",
"type": "iana-if-type:ethernetCsmacd"
}
]
},
"sm-main:root3": {
"my-list": [
{
"name": "list item 1",
"sm-extension:things": [
{
"name": "eth0",
"attribute": 1
}
]
}
]
}
}
```

View file

@ -0,0 +1,8 @@
<cont1 xmlns="urn:module4">
<list>
<leaf1>key_val</leaf1>
<act>
<leaf3>some_output</leaf3>
</act>
</list>
</cont1>

View file

@ -0,0 +1,8 @@
<cont1 xmlns="urn:module4">
<list>
<leaf1>key_val</leaf1>
<act>
<leaf2>some_input</leaf2>
</act>
</list>
</cont1>

View file

@ -0,0 +1,24 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group nc:operation="create">test</group>
<rule>
<name>almighty</name>
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<name>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
</nacm>

View file

@ -0,0 +1,24 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group>test</group>
<rule>
<name>almighty</name>
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<nam>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
</nacm>

View file

@ -0,0 +1,27 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group>test</group>
<rule>
<name>almighty</name>
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<name>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
<denied-operations>0</denied-operations>
<denied-data-writes>0</denied-data-writes>
<denied-notifications>0</denied-notifications>
</nacm>

View file

@ -0,0 +1,27 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group>test</group>
<rule>
<name>almighty</name>
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<name>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
<denied-operations>0</denied-operations>
<denied-data-writes>0</denied-data-writes>
<denied-notifications>0</denied-notifications>
</nacm>

View file

@ -0,0 +1,12 @@
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth0</name>
<description>Wire Connection</description>
<type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
<enabled>true</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<address><ip>192.168.1.15</ip><netmask>255.255.255.0</netmask></address>
<address><ip>192.168.1.10</ip><netmask>255.255.255.0</netmask></address>
</ipv4>
</interface>
</interfaces>

View file

@ -0,0 +1,27 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group>test</group>
<rule>
<nam>almighty
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<name>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
<denied-operations>0</denied-operations>
<denied-data-writes>0</denied-data-writes>
<denied-notifications>0</denied-notifications>
</nacm>

View file

@ -0,0 +1,26 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group>test</group>
<rule>
<name>almighty<module-name></name> *</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<name>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
<denied-operations>0</denied-operations>
<denied-data-writes>0</denied-data-writes>
<denied-notifications>0</denied-notifications>
</nacm>

View file

@ -0,0 +1,26 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group>test</group>
<rule>
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<name>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
<denied-operations>0</denied-operations>
<denied-data-writes>0</denied-data-writes>
<denied-notifications>0</denied-notifications>
</nacm>

View file

@ -0,0 +1,27 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<group>test</group>
<rule>
<name>almighty</name>
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>test</name>
<user-name>smith</user-name>
</group>
<group>
<name>almighty</name>
<user-name>smith</user-name>
<user-name>doe</user-name>
</group>
</groups>
<denied-operations>-1</denied-operations>
<denied-data-writes>0</denied-data-writes>
<denied-notifications>0</denied-notifications>
</nacm>

View file

@ -0,0 +1,29 @@
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<rule-list>
<name>almighty</name>
<group>almighty</group>
<rule>
<name>almighty</name>
<module-name>*</module-name>
<access-operations>*</access-operations>
<action>permit</action>
</rule>
</rule-list>
<groups>
<group>
<name>almighty</name>
<user-name>smith</user-name>
</group>
</groups>
</nacm>
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth0</name>
<description>Wire Connection</description>
<type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
<enabled>true</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<address><ip>192.168.1.15</ip><prefix-length>24</prefix-length></address>
</ipv4>
</interface>
</interfaces>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,725 @@
module ietf-interfaces {
namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
prefix if;
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETMOD (NETCONF Data Modeling Language) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
WG Chair: Thomas Nadeau
<mailto:tnadeau@lucidvision.com>
WG Chair: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"This module contains a collection of YANG definitions for
managing network interfaces.
Copyright (c) 2014 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 7223; see
the RFC itself for full legal notices.";
revision 2014-05-08 {
description
"Initial revision.";
reference
"RFC 7223: A YANG Data Model for Interface Management";
}
/*
* Typedefs
*/
typedef interface-ref {
type leafref {
path "/if:interfaces/if:interface/if:name";
}
description
"This type is used by data models that need to reference
configured interfaces.";
}
typedef interface-state-ref {
type leafref {
path "/if:interfaces-state/if:interface/if:name";
}
description
"This type is used by data models that need to reference
the operationally present interfaces.";
}
/*
* Identities
*/
identity interface-type {
description
"Base identity from which specific interface types are
derived.";
}
/*
* Features
*/
feature arbitrary-names {
description
"This feature indicates that the device allows user-controlled
interfaces to be named arbitrarily.";
}
feature pre-provisioning {
description
"This feature indicates that the device supports
pre-provisioning of interface configuration, i.e., it is
possible to configure an interface whose physical interface
hardware is not present on the device.";
}
feature if-mib {
description
"This feature indicates that the device implements
the IF-MIB.";
reference
"RFC 2863: The Interfaces Group MIB";
}
/*
* Configuration data nodes
*/
container interfaces {
description
"Interface configuration parameters.";
list interface {
key "name";
description
"The list of configured interfaces on the device.
The operational state of an interface is available in the
/interfaces-state/interface list. If the configuration of a
system-controlled interface cannot be used by the system
(e.g., the interface hardware present does not match the
interface type), then the configuration is not applied to
the system-controlled interface shown in the
/interfaces-state/interface list. If the configuration
of a user-controlled interface cannot be used by the system,
the configured interface is not instantiated in the
/interfaces-state/interface list.";
leaf name {
type string;
description
"The name of the interface.
A device MAY restrict the allowed values for this leaf,
possibly depending on the type of the interface.
For system-controlled interfaces, this leaf is the
device-specific name of the interface. The 'config false'
list /interfaces-state/interface contains the currently
existing interfaces on the device.
If a client tries to create configuration for a
system-controlled interface that is not present in the
/interfaces-state/interface list, the server MAY reject
the request if the implementation does not support
pre-provisioning of interfaces or if the name refers to
an interface that can never exist in the system. A
NETCONF server MUST reply with an rpc-error with the
error-tag 'invalid-value' in this case.
If the device supports pre-provisioning of interface
configuration, the 'pre-provisioning' feature is
advertised.
If the device allows arbitrarily named user-controlled
interfaces, the 'arbitrary-names' feature is advertised.
When a configured user-controlled interface is created by
the system, it is instantiated with the same name in the
/interface-state/interface list.";
}
leaf description {
type string;
description
"A textual description of the interface.
A server implementation MAY map this leaf to the ifAlias
MIB object. Such an implementation needs to use some
mechanism to handle the differences in size and characters
allowed between this leaf and ifAlias. The definition of
such a mechanism is outside the scope of this document.
Since ifAlias is defined to be stored in non-volatile
storage, the MIB implementation MUST map ifAlias to the
value of 'description' in the persistently stored
datastore.
Specifically, if the device supports ':startup', when
ifAlias is read the device MUST return the value of
'description' in the 'startup' datastore, and when it is
written, it MUST be written to the 'running' and 'startup'
datastores. Note that it is up to the implementation to
decide whether to modify this single leaf in 'startup' or
perform an implicit copy-config from 'running' to
'startup'.
If the device does not support ':startup', ifAlias MUST
be mapped to the 'description' leaf in the 'running'
datastore.";
reference
"RFC 2863: The Interfaces Group MIB - ifAlias";
}
leaf type {
type identityref {
base interface-type;
}
mandatory true;
description
"The type of the interface.
When an interface entry is created, a server MAY
initialize the type leaf with a valid value, e.g., if it
is possible to derive the type from the name of the
interface.
If a client tries to set the type of an interface to a
value that can never be used by the system, e.g., if the
type is not supported or if the type does not match the
name of the interface, the server MUST reject the request.
A NETCONF server MUST reply with an rpc-error with the
error-tag 'invalid-value' in this case.";
reference
"RFC 2863: The Interfaces Group MIB - ifType";
}
leaf enabled {
type boolean;
default "true";
description
"This leaf contains the configured, desired state of the
interface.
Systems that implement the IF-MIB use the value of this
leaf in the 'running' datastore to set
IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
has been initialized, as described in RFC 2863.
Changes in this leaf in the 'running' datastore are
reflected in ifAdminStatus, but if ifAdminStatus is
changed over SNMP, this leaf is not affected.";
reference
"RFC 2863: The Interfaces Group MIB - ifAdminStatus";
}
leaf link-up-down-trap-enable {
if-feature if-mib;
type enumeration {
enum enabled {
value 1;
}
enum disabled {
value 2;
}
}
description
"Controls whether linkUp/linkDown SNMP notifications
should be generated for this interface.
If this node is not configured, the value 'enabled' is
operationally used by the server for interfaces that do
not operate on top of any other interface (i.e., there are
no 'lower-layer-if' entries), and 'disabled' otherwise.";
reference
"RFC 2863: The Interfaces Group MIB -
ifLinkUpDownTrapEnable";
}
}
}
/*
* Operational state data nodes
*/
container interfaces-state {
config false;
description
"Data nodes for the operational state of interfaces.";
list interface {
key "name";
description
"The list of interfaces on the device.
System-controlled interfaces created by the system are
always present in this list, whether they are configured or
not.";
leaf name {
type string;
description
"The name of the interface.
A server implementation MAY map this leaf to the ifName
MIB object. Such an implementation needs to use some
mechanism to handle the differences in size and characters
allowed between this leaf and ifName. The definition of
such a mechanism is outside the scope of this document.";
reference
"RFC 2863: The Interfaces Group MIB - ifName";
}
leaf type {
type identityref {
base interface-type;
}
mandatory true;
description
"The type of the interface.";
reference
"RFC 2863: The Interfaces Group MIB - ifType";
}
leaf admin-status {
if-feature if-mib;
type enumeration {
enum up {
value 1;
description
"Ready to pass packets.";
}
enum down {
value 2;
description
"Not ready to pass packets and not in some test mode.";
}
enum testing {
value 3;
description
"In some test mode.";
}
}
mandatory true;
description
"The desired state of the interface.
This leaf has the same read semantics as ifAdminStatus.";
reference
"RFC 2863: The Interfaces Group MIB - ifAdminStatus";
}
leaf oper-status {
type enumeration {
enum up {
value 1;
description
"Ready to pass packets.";
}
enum down {
value 2;
description
"The interface does not pass any packets.";
}
enum testing {
value 3;
description
"In some test mode. No operational packets can
be passed.";
}
enum unknown {
value 4;
description
"Status cannot be determined for some reason.";
}
enum dormant {
value 5;
description
"Waiting for some external event.";
}
enum not-present {
value 6;
description
"Some component (typically hardware) is missing.";
}
enum lower-layer-down {
value 7;
description
"Down due to state of lower-layer interface(s).";
}
}
mandatory true;
description
"The current operational state of the interface.
This leaf has the same semantics as ifOperStatus.";
reference
"RFC 2863: The Interfaces Group MIB - ifOperStatus";
}
leaf last-change {
type yang:date-and-time;
description
"The time the interface entered its current operational
state. If the current state was entered prior to the
last re-initialization of the local network management
subsystem, then this node is not present.";
reference
"RFC 2863: The Interfaces Group MIB - ifLastChange";
}
leaf if-index {
if-feature if-mib;
type int32 {
range "1..2147483647";
}
mandatory true;
description
"The ifIndex value for the ifEntry represented by this
interface.";
reference
"RFC 2863: The Interfaces Group MIB - ifIndex";
}
leaf phys-address {
type yang:phys-address;
description
"The interface's address at its protocol sub-layer. For
example, for an 802.x interface, this object normally
contains a Media Access Control (MAC) address. The
interface's media-specific modules must define the bit
and byte ordering and the format of the value of this
object. For interfaces that do not have such an address
(e.g., a serial line), this node is not present.";
reference
"RFC 2863: The Interfaces Group MIB - ifPhysAddress";
}
leaf-list higher-layer-if {
type interface-state-ref;
description
"A list of references to interfaces layered on top of this
interface.";
reference
"RFC 2863: The Interfaces Group MIB - ifStackTable";
}
leaf-list lower-layer-if {
type interface-state-ref;
description
"A list of references to interfaces layered underneath this
interface.";
reference
"RFC 2863: The Interfaces Group MIB - ifStackTable";
}
leaf speed {
type yang:gauge64;
units "bits/second";
description
"An estimate of the interface's current bandwidth in bits
per second. For interfaces that do not vary in
bandwidth or for those where no accurate estimation can
be made, this node should contain the nominal bandwidth.
For interfaces that have no concept of bandwidth, this
node is not present.";
reference
"RFC 2863: The Interfaces Group MIB -
ifSpeed, ifHighSpeed";
}
container statistics {
description
"A collection of interface-related statistics objects.";
leaf discontinuity-time {
type yang:date-and-time;
mandatory true;
description
"The time on the most recent occasion at which any one or
more of this interface's counters suffered a
discontinuity. If no such discontinuities have occurred
since the last re-initialization of the local management
subsystem, then this node contains the time the local
management subsystem re-initialized itself.";
}
leaf in-octets {
type yang:counter64;
description
"The total number of octets received on the interface,
including framing characters.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifHCInOctets";
}
leaf in-unicast-pkts {
type yang:counter64;
description
"The number of packets, delivered by this sub-layer to a
higher (sub-)layer, that were not addressed to a
multicast or broadcast address at this sub-layer.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
}
leaf in-broadcast-pkts {
type yang:counter64;
description
"The number of packets, delivered by this sub-layer to a
higher (sub-)layer, that were addressed to a broadcast
address at this sub-layer.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB -
ifHCInBroadcastPkts";
}
leaf in-multicast-pkts {
type yang:counter64;
description
"The number of packets, delivered by this sub-layer to a
higher (sub-)layer, that were addressed to a multicast
address at this sub-layer. For a MAC-layer protocol,
this includes both Group and Functional addresses.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB -
ifHCInMulticastPkts";
}
leaf in-discards {
type yang:counter32;
description
"The number of inbound packets that were chosen to be
discarded even though no errors had been detected to
prevent their being deliverable to a higher-layer
protocol. One possible reason for discarding such a
packet could be to free up buffer space.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifInDiscards";
}
leaf in-errors {
type yang:counter32;
description
"For packet-oriented interfaces, the number of inbound
packets that contained errors preventing them from being
deliverable to a higher-layer protocol. For character-
oriented or fixed-length interfaces, the number of
inbound transmission units that contained errors
preventing them from being deliverable to a higher-layer
protocol.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifInErrors";
}
leaf in-unknown-protos {
type yang:counter32;
description
"For packet-oriented interfaces, the number of packets
received via the interface that were discarded because
of an unknown or unsupported protocol. For
character-oriented or fixed-length interfaces that
support protocol multiplexing, the number of
transmission units received via the interface that were
discarded because of an unknown or unsupported protocol.
For any interface that does not support protocol
multiplexing, this counter is not present.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
}
leaf out-octets {
type yang:counter64;
description
"The total number of octets transmitted out of the
interface, including framing characters.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
}
leaf out-unicast-pkts {
type yang:counter64;
description
"The total number of packets that higher-level protocols
requested be transmitted, and that were not addressed
to a multicast or broadcast address at this sub-layer,
including those that were discarded or not sent.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
}
leaf out-broadcast-pkts {
type yang:counter64;
description
"The total number of packets that higher-level protocols
requested be transmitted, and that were addressed to a
broadcast address at this sub-layer, including those
that were discarded or not sent.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB -
ifHCOutBroadcastPkts";
}
leaf out-multicast-pkts {
type yang:counter64;
description
"The total number of packets that higher-level protocols
requested be transmitted, and that were addressed to a
multicast address at this sub-layer, including those
that were discarded or not sent. For a MAC-layer
protocol, this includes both Group and Functional
addresses.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB -
ifHCOutMulticastPkts";
}
leaf out-discards {
type yang:counter32;
description
"The number of outbound packets that were chosen to be
discarded even though no errors had been detected to
prevent their being transmitted. One possible reason
for discarding such a packet could be to free up buffer
space.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifOutDiscards";
}
leaf out-errors {
type yang:counter32;
description
"For packet-oriented interfaces, the number of outbound
packets that could not be transmitted because of errors.
For character-oriented or fixed-length interfaces, the
number of outbound transmission units that could not be
transmitted because of errors.
Discontinuities in the value of this counter can occur
at re-initialization of the management system, and at
other times as indicated by the value of
'discontinuity-time'.";
reference
"RFC 2863: The Interfaces Group MIB - ifOutErrors";
}
}
}
}
}

View file

@ -0,0 +1,758 @@
module ietf-ip {
namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
prefix ip;
import ietf-interfaces {
prefix if;
}
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETMOD (NETCONF Data Modeling Language) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
WG Chair: Thomas Nadeau
<mailto:tnadeau@lucidvision.com>
WG Chair: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"This module contains a collection of YANG definitions for
configuring IP implementations.
Copyright (c) 2014 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 7277; see
the RFC itself for full legal notices.";
revision 2014-06-16 {
description
"Initial revision.";
reference
"RFC 7277: A YANG Data Model for IP Management";
}
/*
* Features
*/
feature ipv4-non-contiguous-netmasks {
description
"Indicates support for configuring non-contiguous
subnet masks.";
}
feature ipv6-privacy-autoconf {
description
"Indicates support for Privacy Extensions for Stateless Address
Autoconfiguration in IPv6.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6";
}
/*
* Typedefs
*/
typedef ip-address-origin {
type enumeration {
enum other {
description
"None of the following.";
}
enum static {
description
"Indicates that the address has been statically
configured - for example, using NETCONF or a Command Line
Interface.";
}
enum dhcp {
description
"Indicates an address that has been assigned to this
system by a DHCP server.";
}
enum link-layer {
description
"Indicates an address created by IPv6 stateless
autoconfiguration that embeds a link-layer address in its
interface identifier.";
}
enum random {
description
"Indicates an address chosen by the system at
random, e.g., an IPv4 address within 169.254/16, an
RFC 4941 temporary address, or an RFC 7217 semantically
opaque address.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
RFC 7217: A Method for Generating Semantically Opaque
Interface Identifiers with IPv6 Stateless
Address Autoconfiguration (SLAAC)";
}
}
description
"The origin of an address.";
}
typedef neighbor-origin {
type enumeration {
enum other {
description
"None of the following.";
}
enum static {
description
"Indicates that the mapping has been statically
configured - for example, using NETCONF or a Command Line
Interface.";
}
enum dynamic {
description
"Indicates that the mapping has been dynamically resolved
using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery
protocol.";
}
}
description
"The origin of a neighbor entry.";
}
/*
* Configuration data nodes
*/
augment "/if:interfaces/if:interface" {
description
"Parameters for configuring IP on interfaces.
If an interface is not capable of running IP, the server
must not allow the client to configure these parameters.";
container ipv4 {
presence
"Enables IPv4 unless the 'enabled' leaf
(which defaults to 'true') is set to 'false'";
description
"Parameters for the IPv4 address family.";
leaf enabled {
type boolean;
default true;
description
"Controls whether IPv4 is enabled or disabled on this
interface. When IPv4 is enabled, this interface is
connected to an IPv4 stack, and the interface can send
and receive IPv4 packets.";
}
leaf forwarding {
type boolean;
default false;
description
"Controls IPv4 packet forwarding of datagrams received by,
but not addressed to, this interface. IPv4 routers
forward datagrams. IPv4 hosts do not (except those
source-routed via the host).";
}
leaf mtu {
type uint16 {
range "68..max";
}
units octets;
description
"The size, in octets, of the largest IPv4 packet that the
interface will send and receive.
The server may restrict the allowed values for this leaf,
depending on the interface's type.
If this leaf is not configured, the operationally used MTU
depends on the interface's type.";
reference
"RFC 791: Internet Protocol";
}
list address {
key "ip";
description
"The list of configured IPv4 addresses on the interface.";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address on the interface.";
}
choice subnet {
mandatory true;
description
"The subnet can be specified as a prefix-length, or,
if the server supports non-contiguous netmasks, as
a netmask.";
leaf prefix-length {
type uint8 {
range "0..32";
}
description
"The length of the subnet prefix.";
}
leaf netmask {
if-feature ipv4-non-contiguous-netmasks;
type yang:dotted-quad;
description
"The subnet specified as a netmask.";
}
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv4 addresses to
link-layer addresses.
Entries in this list are used as static entries in the
ARP Cache.";
reference
"RFC 826: An Ethernet Address Resolution Protocol";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
mandatory true;
description
"The link-layer address of the neighbor node.";
}
}
}
container ipv6 {
presence
"Enables IPv6 unless the 'enabled' leaf
(which defaults to 'true') is set to 'false'";
description
"Parameters for the IPv6 address family.";
leaf enabled {
type boolean;
default true;
description
"Controls whether IPv6 is enabled or disabled on this
interface. When IPv6 is enabled, this interface is
connected to an IPv6 stack, and the interface can send
and receive IPv6 packets.";
}
leaf forwarding {
type boolean;
default false;
description
"Controls IPv6 packet forwarding of datagrams received by,
but not addressed to, this interface. IPv6 routers
forward datagrams. IPv6 hosts do not (except those
source-routed via the host).";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 6.2.1, IsRouter";
}
leaf mtu {
type uint32 {
range "1280..max";
}
units octets;
description
"The size, in octets, of the largest IPv6 packet that the
interface will send and receive.
The server may restrict the allowed values for this leaf,
depending on the interface's type.
If this leaf is not configured, the operationally used MTU
depends on the interface's type.";
reference
"RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
Section 5";
}
list address {
key "ip";
description
"The list of configured IPv6 addresses on the interface.";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address on the interface.";
}
leaf prefix-length {
type uint8 {
range "0..128";
}
mandatory true;
description
"The length of the subnet prefix.";
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv6 addresses to
link-layer addresses.
Entries in this list are used as static entries in the
Neighbor Cache.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
mandatory true;
description
"The link-layer address of the neighbor node.";
}
}
leaf dup-addr-detect-transmits {
type uint32;
default 1;
description
"The number of consecutive Neighbor Solicitation messages
sent while performing Duplicate Address Detection on a
tentative address. A value of zero indicates that
Duplicate Address Detection is not performed on
tentative addresses. A value of one indicates a single
transmission with no follow-up retransmissions.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
container autoconf {
description
"Parameters to control the autoconfiguration of IPv6
addresses, as described in RFC 4862.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration";
leaf create-global-addresses {
type boolean;
default true;
description
"If enabled, the host creates global addresses as
described in RFC 4862.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration
Section 5.5";
}
leaf create-temporary-addresses {
if-feature ipv6-privacy-autoconf;
type boolean;
default false;
description
"If enabled, the host creates temporary addresses as
described in RFC 4941.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6";
}
leaf temporary-valid-lifetime {
if-feature ipv6-privacy-autoconf;
type uint32;
units "seconds";
default 604800;
description
"The time period during which the temporary address
is valid.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
- TEMP_VALID_LIFETIME";
}
leaf temporary-preferred-lifetime {
if-feature ipv6-privacy-autoconf;
type uint32;
units "seconds";
default 86400;
description
"The time period during which the temporary address is
preferred.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
- TEMP_PREFERRED_LIFETIME";
}
}
}
}
/*
* Operational state data nodes
*/
augment "/if:interfaces-state/if:interface" {
description
"Data nodes for the operational state of IP on interfaces.";
container ipv4 {
presence "Present if IPv4 is enabled on this interface";
config false;
description
"Interface-specific parameters for the IPv4 address family.";
leaf forwarding {
type boolean;
description
"Indicates whether IPv4 packet forwarding is enabled or
disabled on this interface.";
}
leaf mtu {
type uint16 {
range "68..max";
}
units octets;
description
"The size, in octets, of the largest IPv4 packet that the
interface will send and receive.";
reference
"RFC 791: Internet Protocol";
}
list address {
key "ip";
description
"The list of IPv4 addresses on the interface.";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address on the interface.";
}
choice subnet {
description
"The subnet can be specified as a prefix-length, or,
if the server supports non-contiguous netmasks, as
a netmask.";
leaf prefix-length {
type uint8 {
range "0..32";
}
description
"The length of the subnet prefix.";
}
leaf netmask {
if-feature ipv4-non-contiguous-netmasks;
type yang:dotted-quad;
description
"The subnet specified as a netmask.";
}
}
leaf origin {
type ip-address-origin;
description
"The origin of this address.";
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv4 addresses to
link-layer addresses.
This list represents the ARP Cache.";
reference
"RFC 826: An Ethernet Address Resolution Protocol";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
description
"The origin of this neighbor entry.";
}
}
}
container ipv6 {
presence "Present if IPv6 is enabled on this interface";
config false;
description
"Parameters for the IPv6 address family.";
leaf forwarding {
type boolean;
default false;
description
"Indicates whether IPv6 packet forwarding is enabled or
disabled on this interface.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 6.2.1, IsRouter";
}
leaf mtu {
type uint32 {
range "1280..max";
}
units octets;
description
"The size, in octets, of the largest IPv6 packet that the
interface will send and receive.";
reference
"RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
Section 5";
}
list address {
key "ip";
description
"The list of IPv6 addresses on the interface.";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address on the interface.";
}
leaf prefix-length {
type uint8 {
range "0..128";
}
mandatory true;
description
"The length of the subnet prefix.";
}
leaf origin {
type ip-address-origin;
description
"The origin of this address.";
}
leaf status {
type enumeration {
enum preferred {
description
"This is a valid address that can appear as the
destination or source address of a packet.";
}
enum deprecated {
description
"This is a valid but deprecated address that should
no longer be used as a source address in new
communications, but packets addressed to such an
address are processed as expected.";
}
enum invalid {
description
"This isn't a valid address, and it shouldn't appear
as the destination or source address of a packet.";
}
enum inaccessible {
description
"The address is not accessible because the interface
to which this address is assigned is not
operational.";
}
enum unknown {
description
"The status cannot be determined for some reason.";
}
enum tentative {
description
"The uniqueness of the address on the link is being
verified. Addresses in this state should not be
used for general communication and should only be
used to determine the uniqueness of the address.";
}
enum duplicate {
description
"The address has been determined to be non-unique on
the link and so must not be used.";
}
enum optimistic {
description
"The address is available for use, subject to
restrictions, while its uniqueness on a link is
being verified.";
}
}
description
"The status of an address. Most of the states correspond
to states from the IPv6 Stateless Address
Autoconfiguration protocol.";
reference
"RFC 4293: Management Information Base for the
Internet Protocol (IP)
- IpAddressStatusTC
RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv6 addresses to
link-layer addresses.
This list represents the Neighbor Cache.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
description
"The origin of this neighbor entry.";
}
leaf is-router {
type empty;
description
"Indicates that the neighbor node acts as a router.";
}
leaf state {
type enumeration {
enum incomplete {
description
"Address resolution is in progress, and the link-layer
address of the neighbor has not yet been
determined.";
}
enum reachable {
description
"Roughly speaking, the neighbor is known to have been
reachable recently (within tens of seconds ago).";
}
enum stale {
description
"The neighbor is no longer known to be reachable, but
until traffic is sent to the neighbor no attempt
should be made to verify its reachability.";
}
enum delay {
description
"The neighbor is no longer known to be reachable, and
traffic has recently been sent to the neighbor.
Rather than probe the neighbor immediately, however,
delay sending probes for a short while in order to
give upper-layer protocols a chance to provide
reachability confirmation.";
}
enum probe {
description
"The neighbor is no longer known to be reachable, and
unicast Neighbor Solicitation probes are being sent
to verify reachability.";
}
}
description
"The Neighbor Unreachability Detection state of this
entry.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 7.3.2";
}
}
}
}
}

View file

@ -0,0 +1,412 @@
module ietf-netconf-acm-when {
namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
prefix nacm;
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
WG Chair: Mehmet Ersue
<mailto:mehmet.ersue@nsn.com>
WG Chair: Bert Wijnen
<mailto:bertietf@bwijnen.net>
Editor: Andy Bierman
<mailto:andy@yumaworks.com>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"NETCONF Access Control Model.
Copyright (c) 2012 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6536; see
the RFC itself for full legal notices.";
revision 2012-02-22 {
description
"Initial version";
reference
"RFC 6536: Network Configuration Protocol (NETCONF)
Access Control Model";
}
extension default-deny-write {
description
"Used to indicate that the data model node
represents a sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
write access to the node. An explicit access control rule is
required for all other users.
The 'default-deny-write' extension MAY appear within a data
definition statement. It is ignored otherwise.";
}
extension default-deny-all {
description
"Used to indicate that the data model node
controls a very sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
read, write, or execute access to the node. An explicit
access control rule is required for all other users.
The 'default-deny-all' extension MAY appear within a data
definition statement, 'rpc' statement, or 'notification'
statement. It is ignored otherwise.";
}
typedef user-name-type {
type string {
length "1..max";
}
description
"General Purpose Username string.";
}
typedef matchall-string-type {
type string {
pattern "\\*";
}
description
"The string containing a single asterisk '*' is used
to conceptually represent all possible values
for the particular leaf using this data type.";
}
typedef access-operations-type {
type bits {
bit create {
description
"Any protocol operation that creates a
new data node.";
}
bit read {
description
"Any protocol operation or notification that
returns the value of a data node.";
}
bit update {
description
"Any protocol operation that alters an existing
data node.";
}
bit delete {
description
"Any protocol operation that removes a data node.";
}
bit exec {
description
"Execution access to the specified protocol operation.";
}
}
description
"NETCONF Access Operation.";
}
typedef group-name-type {
type string {
length "1..max";
pattern "[^\\*].*";
}
description
"Name of administrative group to which
users can be assigned.";
}
typedef action-type {
type enumeration {
enum "permit" {
description
"Requested action is permitted.";
}
enum "deny" {
description
"Requested action is denied.";
}
}
description
"Action taken by the server when a particular
rule matches.";
}
typedef node-instance-identifier {
type yang:xpath1.0;
description
"Path expression used to represent a special
data node instance identifier string.
A node-instance-identifier value is an
unrestricted YANG instance-identifier expression.
All the same rules as an instance-identifier apply
except predicates for keys are optional. If a key
predicate is missing, then the node-instance-identifier
represents all possible server instances for that key.
This XPath expression is evaluated in the following context:
o The set of namespace declarations are those in scope on
the leaf element where this type is used.
o The set of variable bindings contains one variable,
'USER', which contains the name of the user of the current
session.
o The function library is the core function library, but
note that due to the syntax restrictions of an
instance-identifier, no functions are allowed.
o The context node is the root node in the data tree.";
}
container nacm {
nacm:default-deny-all;
description
"Parameters for NETCONF Access Control Model.";
leaf enable-nacm {
type boolean;
default "true";
description
"Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.";
}
leaf read-default {
type action-type;
default "permit";
description
"Controls whether read access is granted if
no appropriate rule is found for a
particular read request.";
}
leaf write-default {
type action-type;
default "deny";
description
"Controls whether create, update, or delete access
is granted if no appropriate rule is found for a
particular write request.";
}
leaf exec-default {
type action-type;
default "permit";
description
"Controls whether exec access is granted if no appropriate
rule is found for a particular protocol operation request.";
}
leaf enable-external-groups {
type boolean;
default "true";
description
"Controls whether the server uses the groups reported by the
NETCONF transport layer when it assigns the user to a set of
NACM groups. If this leaf has the value 'false', any group
names reported by the transport layer are ignored by the
server.";
}
leaf denied-operations {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that a
protocol operation request was denied.";
}
leaf denied-data-writes {
type yang:zero-based-counter32;
config false;
mandatory true;
when "../denied-operations > 0";
description
"Number of times since the server last restarted that a
protocol operation request to alter
a configuration datastore was denied.";
}
leaf denied-notifications {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that
a notification was dropped for a subscription because
access to the event type was denied.";
}
container groups {
description
"NETCONF Access Control Groups.";
list group {
key "name";
description
"One NACM Group Entry. This list will only contain
configured entries, not any entries learned from
any transport protocols.";
leaf name {
type group-name-type;
description
"Group name associated with this entry.";
}
leaf-list user-name {
type user-name-type;
description
"Each entry identifies the username of
a member of the group associated with
this entry.";
}
}
}
list rule-list {
key "name";
ordered-by user;
description
"An ordered collection of access control rules.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule-list.";
}
leaf-list group {
type union {
type matchall-string-type;
type group-name-type;
}
description
"List of administrative groups that will be
assigned the associated access rights
defined by the 'rule' list.
The string '*' indicates that all groups apply to the
entry.";
}
list rule {
key "name";
ordered-by user;
description
"One access control rule.
Rules are processed in user-defined order until a match is
found. A rule matches if 'module-name', 'rule-type', and
'access-operations' match the request. If a rule
matches, the 'action' leaf determines if access is granted
or not.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule.";
}
leaf module-name {
type union {
type matchall-string-type;
type string;
}
default "*";
description
"Name of the module associated with this rule.
This leaf matches if it has the value '*' or if the
object being accessed is defined in the module with the
specified module name.";
}
choice rule-type {
description
"This choice matches if all leafs present in the rule
match the request. If no leafs are present, the
choice matches all requests.";
case protocol-operation {
leaf rpc-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if
its value equals the requested protocol operation
name.";
}
}
case notification {
leaf notification-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if its
value equals the requested notification name.";
}
}
case data-node {
leaf path {
type node-instance-identifier;
mandatory true;
description
"Data Node Instance Identifier associated with the
data node controlled by this rule.
Configuration data or state data instance
identifiers start with a top-level data node. A
complete instance identifier is required for this
type of path value.
The special value '/' refers to all possible
datastore contents.";
}
}
}
leaf access-operations {
type union {
type matchall-string-type;
type access-operations-type;
}
default "*";
description
"Access operations associated with this rule.
This leaf matches if it has the value '*' or if the
bit corresponding to the requested operation is set.";
}
leaf action {
type action-type;
mandatory true;
description
"The access control action associated with the
rule. If a rule is determined to match a
particular request, then this object is used
to determine whether to permit or deny the
request.";
}
leaf comment {
type string;
description
"A textual description of the access rule.";
}
}
}
}
}

View file

@ -0,0 +1,447 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:nacm="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types" name="ietf-netconf-acm-when">
<namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"/>
<prefix value="nacm"/>
<import module="ietf-yang-types">
<prefix value="yang"/>
</import>
<organization>
<text>IETF NETCONF (Network Configuration) Working Group</text>
</organization>
<contact>
<text>WG Web: &lt;http://tools.ietf.org/wg/netconf/&gt;
WG List: &lt;mailto:netconf@ietf.org&gt;
WG Chair: Mehmet Ersue
&lt;mailto:mehmet.ersue@nsn.com&gt;
WG Chair: Bert Wijnen
&lt;mailto:bertietf@bwijnen.net&gt;
Editor: Andy Bierman
&lt;mailto:andy@yumaworks.com&gt;
Editor: Martin Bjorklund
&lt;mailto:mbj@tail-f.com&gt;</text>
</contact>
<description>
<text>NETCONF Access Control Model.
Copyright (c) 2012 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6536; see
the RFC itself for full legal notices.</text>
</description>
<revision date="2012-02-22">
<description>
<text>Initial version</text>
</description>
<reference>
<text>RFC 6536: Network Configuration Protocol (NETCONF)
Access Control Model</text>
</reference>
</revision>
<extension name="default-deny-write">
<description>
<text>Used to indicate that the data model node
represents a sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
write access to the node. An explicit access control rule is
required for all other users.
The 'default-deny-write' extension MAY appear within a data
definition statement. It is ignored otherwise.</text>
</description>
</extension>
<extension name="default-deny-all">
<description>
<text>Used to indicate that the data model node
controls a very sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
read, write, or execute access to the node. An explicit
access control rule is required for all other users.
The 'default-deny-all' extension MAY appear within a data
definition statement, 'rpc' statement, or 'notification'
statement. It is ignored otherwise.</text>
</description>
</extension>
<typedef name="user-name-type">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>General Purpose Username string.</text>
</description>
</typedef>
<typedef name="matchall-string-type">
<type name="string">
<pattern value="\*"/>
</type>
<description>
<text>The string containing a single asterisk '*' is used
to conceptually represent all possible values
for the particular leaf using this data type.</text>
</description>
</typedef>
<typedef name="access-operations-type">
<type name="bits">
<bit name="create">
<description>
<text>Any protocol operation that creates a
new data node.</text>
</description>
</bit>
<bit name="read">
<description>
<text>Any protocol operation or notification that
returns the value of a data node.</text>
</description>
</bit>
<bit name="update">
<description>
<text>Any protocol operation that alters an existing
data node.</text>
</description>
</bit>
<bit name="delete">
<description>
<text>Any protocol operation that removes a data node.</text>
</description>
</bit>
<bit name="exec">
<description>
<text>Execution access to the specified protocol operation.</text>
</description>
</bit>
</type>
<description>
<text>NETCONF Access Operation.</text>
</description>
</typedef>
<typedef name="group-name-type">
<type name="string">
<length value="1..max"/>
<pattern value="[^\*].*"/>
</type>
<description>
<text>Name of administrative group to which
users can be assigned.</text>
</description>
</typedef>
<typedef name="action-type">
<type name="enumeration">
<enum name="permit">
<description>
<text>Requested action is permitted.</text>
</description>
</enum>
<enum name="deny">
<description>
<text>Requested action is denied.</text>
</description>
</enum>
</type>
<description>
<text>Action taken by the server when a particular
rule matches.</text>
</description>
</typedef>
<typedef name="node-instance-identifier">
<type name="yang:xpath1.0"/>
<description>
<text>Path expression used to represent a special
data node instance identifier string.
A node-instance-identifier value is an
unrestricted YANG instance-identifier expression.
All the same rules as an instance-identifier apply
except predicates for keys are optional. If a key
predicate is missing, then the node-instance-identifier
represents all possible server instances for that key.
This XPath expression is evaluated in the following context:
o The set of namespace declarations are those in scope on
the leaf element where this type is used.
o The set of variable bindings contains one variable,
'USER', which contains the name of the user of the current
session.
o The function library is the core function library, but
note that due to the syntax restrictions of an
instance-identifier, no functions are allowed.
o The context node is the root node in the data tree.</text>
</description>
</typedef>
<container name="nacm">
<nacm:default-deny-all/>
<description>
<text>Parameters for NETCONF Access Control Model.</text>
</description>
<leaf name="enable-nacm">
<type name="boolean"/>
<default value="true"/>
<description>
<text>Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.</text>
</description>
</leaf>
<leaf name="read-default">
<type name="action-type"/>
<default value="permit"/>
<description>
<text>Controls whether read access is granted if
no appropriate rule is found for a
particular read request.</text>
</description>
</leaf>
<leaf name="write-default">
<type name="action-type"/>
<default value="deny"/>
<description>
<text>Controls whether create, update, or delete access
is granted if no appropriate rule is found for a
particular write request.</text>
</description>
</leaf>
<leaf name="exec-default">
<type name="action-type"/>
<default value="permit"/>
<description>
<text>Controls whether exec access is granted if no appropriate
rule is found for a particular protocol operation request.</text>
</description>
</leaf>
<leaf name="enable-external-groups">
<type name="boolean"/>
<default value="true"/>
<description>
<text>Controls whether the server uses the groups reported by the
NETCONF transport layer when it assigns the user to a set of
NACM groups. If this leaf has the value 'false', any group
names reported by the transport layer are ignored by the
server.</text>
</description>
</leaf>
<leaf name="denied-operations">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<description>
<text>Number of times since the server last restarted that a
protocol operation request was denied.</text>
</description>
</leaf>
<leaf name="denied-data-writes">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<when value="../denied-operations > 0"/>
<description>
<text>Number of times since the server last restarted that a
protocol operation request to alter
a configuration datastore was denied.</text>
</description>
</leaf>
<leaf name="denied-notifications">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<description>
<text>Number of times since the server last restarted that
a notification was dropped for a subscription because
access to the event type was denied.</text>
</description>
</leaf>
<container name="groups">
<description>
<text>NETCONF Access Control Groups.</text>
</description>
<list name="group">
<key value="name"/>
<description>
<text>One NACM Group Entry. This list will only contain
configured entries, not any entries learned from
any transport protocols.</text>
</description>
<leaf name="name">
<type name="group-name-type"/>
<description>
<text>Group name associated with this entry.</text>
</description>
</leaf>
<leaf-list name="user-name">
<type name="user-name-type"/>
<description>
<text>Each entry identifies the username of
a member of the group associated with
this entry.</text>
</description>
</leaf-list>
</list>
</container>
<list name="rule-list">
<key value="name"/>
<ordered-by value="user"/>
<description>
<text>An ordered collection of access control rules.</text>
</description>
<leaf name="name">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>Arbitrary name assigned to the rule-list.</text>
</description>
</leaf>
<leaf-list name="group">
<type name="union">
<type name="matchall-string-type"/>
<type name="group-name-type"/>
</type>
<description>
<text>List of administrative groups that will be
assigned the associated access rights
defined by the 'rule' list.
The string '*' indicates that all groups apply to the
entry.</text>
</description>
</leaf-list>
<list name="rule">
<key value="name"/>
<ordered-by value="user"/>
<description>
<text>One access control rule.
Rules are processed in user-defined order until a match is
found. A rule matches if 'module-name', 'rule-type', and
'access-operations' match the request. If a rule
matches, the 'action' leaf determines if access is granted
or not.</text>
</description>
<leaf name="name">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>Arbitrary name assigned to the rule.</text>
</description>
</leaf>
<leaf name="module-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<default value="*"/>
<description>
<text>Name of the module associated with this rule.
This leaf matches if it has the value '*' or if the
object being accessed is defined in the module with the
specified module name.</text>
</description>
</leaf>
<choice name="rule-type">
<description>
<text>This choice matches if all leafs present in the rule
match the request. If no leafs are present, the
choice matches all requests.</text>
</description>
<case name="protocol-operation">
<leaf name="rpc-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<description>
<text>This leaf matches if it has the value '*' or if
its value equals the requested protocol operation
name.</text>
</description>
</leaf>
</case>
<case name="notification">
<leaf name="notification-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<description>
<text>This leaf matches if it has the value '*' or if its
value equals the requested notification name.</text>
</description>
</leaf>
</case>
<case name="data-node">
<leaf name="path">
<type name="node-instance-identifier"/>
<mandatory value="true"/>
<description>
<text>Data Node Instance Identifier associated with the
data node controlled by this rule.
Configuration data or state data instance
identifiers start with a top-level data node. A
complete instance identifier is required for this
type of path value.
The special value '/' refers to all possible
datastore contents.</text>
</description>
</leaf>
</case>
</choice>
<leaf name="access-operations">
<type name="union">
<type name="matchall-string-type"/>
<type name="access-operations-type"/>
</type>
<default value="*"/>
<description>
<text>Access operations associated with this rule.
This leaf matches if it has the value '*' or if the
bit corresponding to the requested operation is set.</text>
</description>
</leaf>
<leaf name="action">
<type name="action-type"/>
<mandatory value="true"/>
<description>
<text>The access control action associated with the
rule. If a rule is determined to match a
particular request, then this object is used
to determine whether to permit or deny the
request.</text>
</description>
</leaf>
<leaf name="comment">
<type name="string"/>
<description>
<text>A textual description of the access rule.</text>
</description>
</leaf>
</list>
</list>
</container>
</module>

View file

@ -0,0 +1,447 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:nacm="urn:ietf:params:xml:ns:yang:ietf-netconf-acm" xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types" name="ietf-netconf-acm-when2">
<namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"/>
<prefix value="nacm"/>
<import module="ietf-yang-types">
<prefix value="yang"/>
</import>
<organization>
<text>IETF NETCONF (Network Configuration) Working Group</text>
</organization>
<contact>
<text>WG Web: &lt;http://tools.ietf.org/wg/netconf/&gt;
WG List: &lt;mailto:netconf@ietf.org&gt;
WG Chair: Mehmet Ersue
&lt;mailto:mehmet.ersue@nsn.com&gt;
WG Chair: Bert Wijnen
&lt;mailto:bertietf@bwijnen.net&gt;
Editor: Andy Bierman
&lt;mailto:andy@yumaworks.com&gt;
Editor: Martin Bjorklund
&lt;mailto:mbj@tail-f.com&gt;</text>
</contact>
<description>
<text>NETCONF Access Control Model.
Copyright (c) 2012 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6536; see
the RFC itself for full legal notices.</text>
</description>
<revision date="2012-02-22">
<description>
<text>Initial version</text>
</description>
<reference>
<text>RFC 6536: Network Configuration Protocol (NETCONF)
Access Control Model</text>
</reference>
</revision>
<extension name="default-deny-write">
<description>
<text>Used to indicate that the data model node
represents a sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
write access to the node. An explicit access control rule is
required for all other users.
The 'default-deny-write' extension MAY appear within a data
definition statement. It is ignored otherwise.</text>
</description>
</extension>
<extension name="default-deny-all">
<description>
<text>Used to indicate that the data model node
controls a very sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
read, write, or execute access to the node. An explicit
access control rule is required for all other users.
The 'default-deny-all' extension MAY appear within a data
definition statement, 'rpc' statement, or 'notification'
statement. It is ignored otherwise.</text>
</description>
</extension>
<typedef name="user-name-type">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>General Purpose Username string.</text>
</description>
</typedef>
<typedef name="matchall-string-type">
<type name="string">
<pattern value="\*"/>
</type>
<description>
<text>The string containing a single asterisk '*' is used
to conceptually represent all possible values
for the particular leaf using this data type.</text>
</description>
</typedef>
<typedef name="access-operations-type">
<type name="bits">
<bit name="create">
<description>
<text>Any protocol operation that creates a
new data node.</text>
</description>
</bit>
<bit name="read">
<description>
<text>Any protocol operation or notification that
returns the value of a data node.</text>
</description>
</bit>
<bit name="update">
<description>
<text>Any protocol operation that alters an existing
data node.</text>
</description>
</bit>
<bit name="delete">
<description>
<text>Any protocol operation that removes a data node.</text>
</description>
</bit>
<bit name="exec">
<description>
<text>Execution access to the specified protocol operation.</text>
</description>
</bit>
</type>
<description>
<text>NETCONF Access Operation.</text>
</description>
</typedef>
<typedef name="group-name-type">
<type name="string">
<length value="1..max"/>
<pattern value="[^\*].*"/>
</type>
<description>
<text>Name of administrative group to which
users can be assigned.</text>
</description>
</typedef>
<typedef name="action-type">
<type name="enumeration">
<enum name="permit">
<description>
<text>Requested action is permitted.</text>
</description>
</enum>
<enum name="deny">
<description>
<text>Requested action is denied.</text>
</description>
</enum>
</type>
<description>
<text>Action taken by the server when a particular
rule matches.</text>
</description>
</typedef>
<typedef name="node-instance-identifier">
<type name="yang:xpath1.0"/>
<description>
<text>Path expression used to represent a special
data node instance identifier string.
A node-instance-identifier value is an
unrestricted YANG instance-identifier expression.
All the same rules as an instance-identifier apply
except predicates for keys are optional. If a key
predicate is missing, then the node-instance-identifier
represents all possible server instances for that key.
This XPath expression is evaluated in the following context:
o The set of namespace declarations are those in scope on
the leaf element where this type is used.
o The set of variable bindings contains one variable,
'USER', which contains the name of the user of the current
session.
o The function library is the core function library, but
note that due to the syntax restrictions of an
instance-identifier, no functions are allowed.
o The context node is the root node in the data tree.</text>
</description>
</typedef>
<container name="nacm">
<nacm:default-deny-all/>
<description>
<text>Parameters for NETCONF Access Control Model.</text>
</description>
<leaf name="enable-nacm">
<type name="boolean"/>
<default value="true"/>
<description>
<text>Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.</text>
</description>
</leaf>
<leaf name="read-default">
<type name="action-type"/>
<default value="permit"/>
<description>
<text>Controls whether read access is granted if
no appropriate rule is found for a
particular read request.</text>
</description>
</leaf>
<leaf name="write-default">
<type name="action-type"/>
<default value="deny"/>
<description>
<text>Controls whether create, update, or delete access
is granted if no appropriate rule is found for a
particular write request.</text>
</description>
</leaf>
<leaf name="exec-default">
<type name="action-type"/>
<default value="permit"/>
<description>
<text>Controls whether exec access is granted if no appropriate
rule is found for a particular protocol operation request.</text>
</description>
</leaf>
<leaf name="enable-external-groups">
<type name="boolean"/>
<default value="true"/>
<description>
<text>Controls whether the server uses the groups reported by the
NETCONF transport layer when it assigns the user to a set of
NACM groups. If this leaf has the value 'false', any group
names reported by the transport layer are ignored by the
server.</text>
</description>
</leaf>
<leaf name="denied-operations">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<description>
<text>Number of times since the server last restarted that a
protocol operation request was denied.</text>
</description>
</leaf>
<leaf name="denied-data-writes">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<when condition="../denied-operations > 0"/>
<description>
<text>Number of times since the server last restarted that a
protocol operation request to alter
a configuration datastore was denied.</text>
</description>
</leaf>
<leaf name="denied-notifications">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<description>
<text>Number of times since the server last restarted that
a notification was dropped for a subscription because
access to the event type was denied.</text>
</description>
</leaf>
<container name="groups">
<description>
<text>NETCONF Access Control Groups.</text>
</description>
<list name="group">
<key value="name"/>
<description>
<text>One NACM Group Entry. This list will only contain
configured entries, not any entries learned from
any transport protocols.</text>
</description>
<leaf name="name">
<type name="group-name-type"/>
<description>
<text>Group name associated with this entry.</text>
</description>
</leaf>
<leaf-list name="user-name">
<type name="user-name-type"/>
<description>
<text>Each entry identifies the username of
a member of the group associated with
this entry.</text>
</description>
</leaf-list>
</list>
</container>
<list name="rule-list">
<key value="name"/>
<ordered-by value="user"/>
<description>
<text>An ordered collection of access control rules.</text>
</description>
<leaf name="name">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>Arbitrary name assigned to the rule-list.</text>
</description>
</leaf>
<leaf-list name="group">
<type name="union">
<type name="matchall-string-type"/>
<type name="group-name-type"/>
</type>
<description>
<text>List of administrative groups that will be
assigned the associated access rights
defined by the 'rule' list.
The string '*' indicates that all groups apply to the
entry.</text>
</description>
</leaf-list>
<list name="rule">
<key value="name"/>
<ordered-by value="user"/>
<description>
<text>One access control rule.
Rules are processed in user-defined order until a match is
found. A rule matches if 'module-name', 'rule-type', and
'access-operations' match the request. If a rule
matches, the 'action' leaf determines if access is granted
or not.</text>
</description>
<leaf name="name">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>Arbitrary name assigned to the rule.</text>
</description>
</leaf>
<leaf name="module-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<default value="*"/>
<description>
<text>Name of the module associated with this rule.
This leaf matches if it has the value '*' or if the
object being accessed is defined in the module with the
specified module name.</text>
</description>
</leaf>
<choice name="rule-type">
<description>
<text>This choice matches if all leafs present in the rule
match the request. If no leafs are present, the
choice matches all requests.</text>
</description>
<case name="protocol-operation">
<leaf name="rpc-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<description>
<text>This leaf matches if it has the value '*' or if
its value equals the requested protocol operation
name.</text>
</description>
</leaf>
</case>
<case name="notification">
<leaf name="notification-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<description>
<text>This leaf matches if it has the value '*' or if its
value equals the requested notification name.</text>
</description>
</leaf>
</case>
<case name="data-node">
<leaf name="path">
<type name="node-instance-identifier"/>
<mandatory value="true"/>
<description>
<text>Data Node Instance Identifier associated with the
data node controlled by this rule.
Configuration data or state data instance
identifiers start with a top-level data node. A
complete instance identifier is required for this
type of path value.
The special value '/' refers to all possible
datastore contents.</text>
</description>
</leaf>
</case>
</choice>
<leaf name="access-operations">
<type name="union">
<type name="matchall-string-type"/>
<type name="access-operations-type"/>
</type>
<default value="*"/>
<description>
<text>Access operations associated with this rule.
This leaf matches if it has the value '*' or if the
bit corresponding to the requested operation is set.</text>
</description>
</leaf>
<leaf name="action">
<type name="action-type"/>
<mandatory value="true"/>
<description>
<text>The access control action associated with the
rule. If a rule is determined to match a
particular request, then this object is used
to determine whether to permit or deny the
request.</text>
</description>
</leaf>
<leaf name="comment">
<type name="string"/>
<description>
<text>A textual description of the access rule.</text>
</description>
</leaf>
</list>
</list>
</container>
</module>

View file

@ -0,0 +1,411 @@
module ietf-netconf-acm {
namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
prefix nacm;
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
WG Chair: Mehmet Ersue
<mailto:mehmet.ersue@nsn.com>
WG Chair: Bert Wijnen
<mailto:bertietf@bwijnen.net>
Editor: Andy Bierman
<mailto:andy@yumaworks.com>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"NETCONF Access Control Model.
Copyright (c) 2012 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6536; see
the RFC itself for full legal notices.";
revision 2012-02-22 {
description
"Initial version";
reference
"RFC 6536: Network Configuration Protocol (NETCONF)
Access Control Model";
}
extension default-deny-write {
description
"Used to indicate that the data model node
represents a sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
write access to the node. An explicit access control rule is
required for all other users.
The 'default-deny-write' extension MAY appear within a data
definition statement. It is ignored otherwise.";
}
extension default-deny-all {
description
"Used to indicate that the data model node
controls a very sensitive security system parameter.
If present, and the NACM module is enabled (i.e.,
/nacm/enable-nacm object equals 'true'), the NETCONF server
will only allow the designated 'recovery session' to have
read, write, or execute access to the node. An explicit
access control rule is required for all other users.
The 'default-deny-all' extension MAY appear within a data
definition statement, 'rpc' statement, or 'notification'
statement. It is ignored otherwise.";
}
typedef user-name-type {
type string {
length "1..max";
}
description
"General Purpose Username string.";
}
typedef matchall-string-type {
type string {
pattern "\\*";
}
description
"The string containing a single asterisk '*' is used
to conceptually represent all possible values
for the particular leaf using this data type.";
}
typedef access-operations-type {
type bits {
bit create {
description
"Any protocol operation that creates a
new data node.";
}
bit read {
description
"Any protocol operation or notification that
returns the value of a data node.";
}
bit update {
description
"Any protocol operation that alters an existing
data node.";
}
bit delete {
description
"Any protocol operation that removes a data node.";
}
bit exec {
description
"Execution access to the specified protocol operation.";
}
}
description
"NETCONF Access Operation.";
}
typedef group-name-type {
type string {
length "1..max";
pattern "[^\\*].*";
}
description
"Name of administrative group to which
users can be assigned.";
}
typedef action-type {
type enumeration {
enum "permit" {
description
"Requested action is permitted.";
}
enum "deny" {
description
"Requested action is denied.";
}
}
description
"Action taken by the server when a particular
rule matches.";
}
typedef node-instance-identifier {
type yang:xpath1.0;
description
"Path expression used to represent a special
data node instance identifier string.
A node-instance-identifier value is an
unrestricted YANG instance-identifier expression.
All the same rules as an instance-identifier apply
except predicates for keys are optional. If a key
predicate is missing, then the node-instance-identifier
represents all possible server instances for that key.
This XPath expression is evaluated in the following context:
o The set of namespace declarations are those in scope on
the leaf element where this type is used.
o The set of variable bindings contains one variable,
'USER', which contains the name of the user of the current
session.
o The function library is the core function library, but
note that due to the syntax restrictions of an
instance-identifier, no functions are allowed.
o The context node is the root node in the data tree.";
}
container nacm {
nacm:default-deny-all;
description
"Parameters for NETCONF Access Control Model.";
leaf enable-nacm {
type boolean;
default "true";
description
"Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.";
}
leaf read-default {
type action-type;
default "permit";
description
"Controls whether read access is granted if
no appropriate rule is found for a
particular read request.";
}
leaf write-default {
type action-type;
default "deny";
description
"Controls whether create, update, or delete access
is granted if no appropriate rule is found for a
particular write request.";
}
leaf exec-default {
type action-type;
default "permit";
description
"Controls whether exec access is granted if no appropriate
rule is found for a particular protocol operation request.";
}
leaf enable-external-groups {
type boolean;
default "true";
description
"Controls whether the server uses the groups reported by the
NETCONF transport layer when it assigns the user to a set of
NACM groups. If this leaf has the value 'false', any group
names reported by the transport layer are ignored by the
server.";
}
leaf denied-operations {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that a
protocol operation request was denied.";
}
leaf denied-data-writes {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that a
protocol operation request to alter
a configuration datastore was denied.";
}
leaf denied-notifications {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that
a notification was dropped for a subscription because
access to the event type was denied.";
}
container groups {
description
"NETCONF Access Control Groups.";
list group {
key "name";
description
"One NACM Group Entry. This list will only contain
configured entries, not any entries learned from
any transport protocols.";
leaf name {
type group-name-type;
description
"Group name associated with this entry.";
}
leaf-list user-name {
type user-name-type;
description
"Each entry identifies the username of
a member of the group associated with
this entry.";
}
}
}
list rule-list {
key "name";
ordered-by user;
description
"An ordered collection of access control rules.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule-list.";
}
leaf-list group {
type union {
type matchall-string-type;
type group-name-type;
}
description
"List of administrative groups that will be
assigned the associated access rights
defined by the 'rule' list.
The string '*' indicates that all groups apply to the
entry.";
}
list rule {
key "name";
ordered-by user;
description
"One access control rule.
Rules are processed in user-defined order until a match is
found. A rule matches if 'module-name', 'rule-type', and
'access-operations' match the request. If a rule
matches, the 'action' leaf determines if access is granted
or not.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule.";
}
leaf module-name {
type union {
type matchall-string-type;
type string;
}
default "*";
description
"Name of the module associated with this rule.
This leaf matches if it has the value '*' or if the
object being accessed is defined in the module with the
specified module name.";
}
choice rule-type {
description
"This choice matches if all leafs present in the rule
match the request. If no leafs are present, the
choice matches all requests.";
case protocol-operation {
leaf rpc-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if
its value equals the requested protocol operation
name.";
}
}
case notification {
leaf notification-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if its
value equals the requested notification name.";
}
}
case data-node {
leaf path {
type node-instance-identifier;
mandatory true;
description
"Data Node Instance Identifier associated with the
data node controlled by this rule.
Configuration data or state data instance
identifiers start with a top-level data node. A
complete instance identifier is required for this
type of path value.
The special value '/' refers to all possible
datastore contents.";
}
}
}
leaf access-operations {
type union {
type matchall-string-type;
type access-operations-type;
}
default "*";
description
"Access operations associated with this rule.
This leaf matches if it has the value '*' or if the
bit corresponding to the requested operation is set.";
}
leaf action {
type action-type;
mandatory true;
description
"The access control action associated with the
rule. If a rule is determined to match a
particular request, then this object is used
to determine whether to permit or deny the
request.";
}
leaf comment {
type string;
description
"A textual description of the access rule.";
}
}
}
}
}

View file

@ -0,0 +1,5 @@
module module1 {
namespace "urn:yanglint:module";
prefix m;
leaf m { type string; }
}

View file

@ -0,0 +1,5 @@
module module1b {
namespace "urn:yanglint:module";
prefix m;
leaf mb { type string; }
}

View file

@ -0,0 +1,5 @@
module module2 {
namespace "urn:yanglint:module";
prefix m;
leaf m { ttype string; }
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module name="module2"
xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:m="urn:yanglint:module">
<namespace uri="urn:yanglint:module"/>
<prefix value="m"/>
<leaf name="m">
<type value="string"/>
</leaf>
</module>

View file

@ -0,0 +1,8 @@
module module3 {
namespace "urn:yanglint:module";
prefix m;
leaf m { type string; must "../c/a"; }
container c {
leaf b { type string; }
}
}

View file

@ -0,0 +1,52 @@
module module4 {
yang-version 1.1;
namespace "urn:module4";
prefix m4;
container cont1 {
list list {
key "leaf1";
leaf leaf1 {
type string;
}
action act {
input {
leaf leaf2 {
type string;
}
}
output {
leaf leaf3 {
type string;
}
}
}
notification notif1 {
leaf leaf4 {
type string;
}
}
}
}
rpc rpc {
input {
leaf leaf5 {
type string;
}
}
output {
container cont2 {
leaf leaf6 {
type empty;
}
}
}
}
notification notif2 {
leaf leaf7 {
type empty;
}
}
}

View file

@ -0,0 +1,8 @@
<cont1 xmlns="urn:module4">
<list>
<leaf1>key_val</leaf1>
<notif1>
<leaf4>some_value</leaf4>
</notif1>
</list>
</cont1>

View file

@ -0,0 +1,3 @@
<notif2 xmlns="urn:module4">
<leaf7/>
</notif2>

View file

@ -0,0 +1,5 @@
<rpc xmlns="urn:module4">
<cont2>
<leaf6/>
</cont2>
</rpc>

View file

@ -0,0 +1,3 @@
<rpc xmlns="urn:module4">
<leaf5>some_input</leaf5>
</rpc>

View file

@ -0,0 +1,64 @@
<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"
xmlns:ds="urn:ietf:params:xml:ns:yang:ietf-datastores">
<module-set>
<name>test-set</name>
<module>
<name>ietf-datastores</name>
<revision>2018-02-14</revision>
<namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>
</module>
<module>
<name>ietf-yang-library</name>
<revision>2019-01-04</revision>
<namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>
</module>
<module>
<name>sm-extension</name>
<namespace>urn:sm-ext</namespace>
</module>
<module>
<name>iana-if-type</name>
<namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>
</module>
<import-only-module>
<name>ietf-yang-types</name>
<revision>2013-07-15</revision>
<namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>
</import-only-module>
<import-only-module>
<name>sm-mod</name>
<revision>2017-01-26</revision>
<namespace>urn:yanglint:sm-mod</namespace>
</import-only-module>
</module-set>
<schema>
<name>test-schema</name>
<module-set>test-set</module-set>
</schema>
<datastore>
<name>ds:running</name>
<schema>test-schema</schema>
</datastore>
<datastore>
<name>ds:operational</name>
<schema>test-schema</schema>
</datastore>
<content-id>1</content-id>
</yang-library>
<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
<module-set-id>1</module-set-id>
</modules-state>
<schema-mounts xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount">
<namespace>
<prefix>if</prefix>
<uri>urn:ietf:params:xml:ns:yang:ietf-interfaces</uri>
</namespace>
<mount-point>
<module>sm-main</module>
<label>mnt-root</label>
<shared-schema>
<parent-reference>/if:interfaces/if:interface/if:name</parent-reference>
<parent-reference>/if:interfaces/if:interface/if:type</parent-reference>
</shared-schema>
</mount-point>
</schema-mounts>

View file

@ -0,0 +1,54 @@
<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"
xmlns:ds="urn:ietf:params:xml:ns:yang:ietf-datastores">
<module-set>
<name>main-set</name>
<module>
<name>ietf-datastores</name>
<revision>2018-02-14</revision>
<namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>
</module>
<module>
<name>ietf-yang-library</name>
<revision>2019-01-04</revision>
<namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>
</module>
<module>
<name>ietf-yang-schema-mount</name>
<revision>2019-01-14</revision>
<namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>
</module>
<module>
<name>sm-main</name>
<namespace>urn:sm-main</namespace>
</module>
<module>
<name>iana-if-type</name>
<namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>
</module>
<module>
<name>ietf-interfaces</name>
<namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>
</module>
<import-only-module>
<name>ietf-yang-types</name>
<revision>2013-07-15</revision>
<namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>
</import-only-module>
</module-set>
<schema>
<name>main-schema</name>
<module-set>main-set</module-set>
</schema>
<datastore>
<name>ds:running</name>
<schema>main-schema</schema>
</datastore>
<datastore>
<name>ds:operational</name>
<schema>main-schema</schema>
</datastore>
<content-id>1</content-id>
</yang-library>
<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
<module-set-id>2</module-set-id>
</modules-state>

View file

@ -0,0 +1,19 @@
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth0</name>
<type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
</interface>
<interface>
<name>eth1</name>
<type xmlns:ift="urn:ietf:params:xml:ns:yang:iana-if-type">ift:ethernetCsmacd</type>
</interface>
</interfaces>
<root3 xmlns="urn:sm-main">
<my-list>
<name>list item 1</name>
<things xmlns="urn:sm-ext">
<name>eth0</name>
<attribute>1</attribute>
</things>
</my-list>
</root3>

View file

@ -0,0 +1,39 @@
module sm-extension {
yang-version 1.1;
namespace "urn:sm-ext";
prefix "sm-ext";
import ietf-interfaces {
prefix if;
}
import sm-mod {
prefix sm-mod;
}
revision 2022-09-15 {
description
"initial";
reference
"";
}
list things {
key "name";
leaf name {
type leafref {
path "/if:interfaces/if:interface/if:name";
}
}
leaf attribute {
type uint32;
}
}
augment "/if:interfaces/if:interface" {
leaf thing-attribute {
type leafref {
path "/things/attribute";
}
}
}
}

View file

@ -0,0 +1,32 @@
module sm-main {
yang-version 1.1;
namespace "urn:sm-main";
prefix "sm-main";
import ietf-yang-schema-mount {
prefix yangmnt;
}
import ietf-interfaces {
prefix if;
}
list root {
key "node";
leaf node {
type string;
}
yangmnt:mount-point "root";
}
container root2 {
yangmnt:mount-point "root";
}
container root3 {
list my-list {
key name;
leaf name {
type string;
}
yangmnt:mount-point "mnt-root";
}
}
}

View file

@ -0,0 +1,21 @@
module sm-mod {
yang-version 1.1;
namespace "urn:yanglint:sm-mod";
prefix "sm-mod";
revision 2017-01-26 {
description
"initial";
reference
"";
}
container not-compiled {
leaf first {
type string;
}
leaf second {
type string;
}
}
}

View file

@ -0,0 +1,25 @@
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,94 @@
/* linenoise.h -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
struct linenoiseState {
int ifd; /* Terminal stdin file descriptor. */
int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
int rawmode;
int history_index; /* The history index we are currently editing. */
};
extern struct linenoiseState lss;
typedef struct linenoiseCompletions {
int path;
size_t len;
char **cvec;
} linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(const char *, const char *, linenoiseCompletions *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
char *linenoise(const char *prompt);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
void linenoisePathCompletion(const char *, const char *, linenoiseCompletions *);
void linenoiseRefreshLine(void);
int linenoiseEnableRawMode(int fd);
void linenoiseDisableRawMode(int fd);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */

134
tools/lint/main.c Normal file
View file

@ -0,0 +1,134 @@
/**
* @file main.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool
*
* Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L /* strdup */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libyang.h"
#include "cmd.h"
#include "common.h"
#include "completion.h"
#include "configuration.h"
#include "linenoise/linenoise.h"
#include "yl_opt.h"
int done;
struct ly_ctx *ctx = NULL;
/* main_ni.c */
int main_ni(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
int cmdlen, posc, i, j;
struct yl_opt yo = {0};
char *empty = NULL, *cmdline;
char **posv;
uint8_t cmd_found;
if (argc > 1) {
/* run in non-interactive mode */
return main_ni(argc, argv);
}
yo.interactive = 1;
/* continue in interactive mode */
linenoiseSetCompletionCallback(complete_cmd);
load_config();
if (ly_ctx_new(NULL, YL_DEFAULT_CTX_OPTIONS, &ctx)) {
YLMSG_E("Failed to create context.");
return 1;
}
while (!done) {
cmd_found = 0;
posv = &empty;
posc = 0;
/* get the command from user */
cmdline = linenoise(PROMPT);
/* EOF -> exit */
if (cmdline == NULL) {
done = 1;
cmdline = strdup("quit");
}
/* empty line -> wait for another command */
if (*cmdline == '\0') {
free(cmdline);
continue;
}
/* isolate the command word. */
for (cmdlen = 0; cmdline[cmdlen] && (cmdline[cmdlen] != ' '); cmdlen++) {}
/* execute the command if any valid specified */
for (i = 0; commands[i].name; i++) {
if (strncmp(cmdline, commands[i].name, (size_t)cmdlen) || (commands[i].name[cmdlen] != '\0')) {
continue;
}
cmd_found = 1;
if (commands[i].opt_func && commands[i].opt_func(&yo, cmdline, &posv, &posc)) {
break;
}
if (commands[i].dep_func && commands[i].dep_func(&yo, posc)) {
break;
}
if (posc) {
for (j = 0; j < posc; j++) {
yo.last_one = (j + 1) == posc;
if (commands[i].exec_func(&ctx, &yo, posv[j])) {
break;
}
}
} else {
commands[i].exec_func(&ctx, &yo, NULL);
}
if (commands[i].fin_func) {
commands[i].fin_func(ctx, &yo);
}
break;
}
if (!cmd_found) {
/* if unknown command specified, tell it to user */
YLMSG_E("Unknown command \"%.*s\", type 'help' for more information.", cmdlen, cmdline);
}
linenoiseHistoryAdd(cmdline);
free(cmdline);
yl_opt_erase(&yo);
}
/* Global variables in commands are freed. */
cmd_free();
store_config();
ly_ctx_destroy(ctx);
return 0;
}

790
tools/lint/main_ni.c Normal file
View file

@ -0,0 +1,790 @@
/**
* @file main_ni.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - non-interactive code
*
* Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include "libyang.h"
#include "cmd.h"
#include "common.h"
#include "out.h"
#include "tools/config.h"
#include "yl_opt.h"
#include "yl_schema_features.h"
static void
version(void)
{
printf("yanglint %s\n", PROJECT_VERSION);
}
static void
help(int shortout)
{
printf("Example usage:\n"
" yanglint [-f { yang | yin | info}] <schema>...\n"
" Validates the YANG module <schema>(s) and all its dependencies, optionally printing\n"
" them in the specified format.\n\n"
" yanglint [-f { xml | json }] <schema>... <file>...\n"
" Validates the YANG modeled data <file>(s) according to the <schema>(s) optionally\n"
" printing them in the specified format.\n\n"
" yanglint -t (nc-)rpc/notif [-O <operational-file>] <schema>... <file>\n"
" Validates the YANG/NETCONF RPC/notification <file> according to the <schema>(s) using\n"
" <operational-file> with possible references to the operational datastore data.\n"
" To validate nested-notification or action, the <operational-file> is required.\n\n"
" yanglint -t nc-reply -R <rpc-file> [-O <operational-file>] <schema>... <file>\n"
" Validates the NETCONF rpc-reply <file> of RPC <rpc-file> according to the <schema>(s)\n"
" using <operational-file> with possible references to the operational datastore data.\n\n"
" yanglint\n"
" Starts interactive mode with more features.\n\n");
if (shortout) {
return;
}
printf("Options:\n"
" -h, --help Show this help message and exit.\n"
" -v, --version Show version number and exit.\n"
" -V, --verbose Increase libyang verbosity and show verbose messages. If specified\n"
" a second time, show even debug messages.\n"
" -Q, --quiet Decrease libyang verbosity and hide warnings. If specified a second\n"
" time, hide errors so no libyang messages are printed.\n");
printf(" -f FORMAT, --format=FORMAT\n"
" Convert input into FORMAT. Supported formats: \n"
" yang, yin, tree, info and feature-param for schemas,\n"
" xml, json, and lyb for data.\n\n");
printf(" -I FORMAT, --in-format=FORMAT\n"
" Load the data in one of the following formats:\n"
" xml, json, lyb\n"
" If input format not specified, it is detected from the file extension.\n\n");
printf(" -p PATH, --path=PATH\n"
" Search path for schema (YANG/YIN) modules. The option can be\n"
" used multiple times. The current working directory and the\n"
" path of the module being added is used implicitly. Subdirectories\n"
" are also searched\n\n");
printf(" -D, --disable-searchdir\n"
" Do not implicitly search in current working directory for\n"
" schema modules. If specified a second time, do not even\n"
" search in the module directory (all modules must be \n"
" explicitly specified).\n\n");
printf(" -F FEATURES, --features=FEATURES\n"
" Specific module features to support in the form <module-name>:(<feature>,)*\n"
" Use <feature> '*' to enable all features of a module. This option can be\n"
" specified multiple times, to enable features in multiple modules. If this\n"
" option is not specified, all the features in all the implemented modules\n"
" are enabled.\n\n");
printf(" -i, --make-implemented\n"
" Make the imported modules \"referenced\" from any loaded\n"
" module also implemented. If specified a second time, all the\n"
" modules are set implemented.\n\n");
printf(" -P PATH, --schema-node=PATH\n"
" Print only the specified subtree of the schema.\n"
" The PATH is the XPath subset mentioned in documentation as\n"
" the Path format. The option can be combined with --single-node\n"
" option to print information only about the specified node.\n"
" -q, --single-node\n"
" Supplement to the --schema-node option to print information\n"
" only about a single node specified as PATH argument.\n\n");
printf(" -s SUBMODULE, --submodule=SUBMODULE\n"
" Print the specific submodule instead of the main module.\n\n");
printf(" -x FILE, --ext-data=FILE\n"
" File containing the specific data required by an extension. Required by\n"
" the schema-mount extension, for example, when the operational data are\n"
" expected in the file. File format is guessed.\n\n");
printf(" -n, --not-strict\n"
" Do not require strict data parsing (silently skip unknown data),\n"
" has no effect for schemas.\n\n");
printf(" -e, --present Validate only with the schema modules whose data actually\n"
" exist in the provided input data files. Takes effect only\n"
" with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
" mandatory nodes from modules which data are not present in the\n"
" provided input data files.\n\n");
printf(" -t TYPE, --type=TYPE\n"
" Specify data tree type in the input data file(s):\n"
" data - Complete datastore with status data (default type).\n"
" config - Configuration datastore (without status data).\n"
" get - Data returned by the NETCONF <get> operation.\n"
" getconfig - Data returned by the NETCONF <get-config> operation.\n"
" edit - Config content of the NETCONF <edit-config> operation.\n"
" rpc - Invocation of a YANG RPC/action, defined as input.\n"
" nc-rpc - Similar to 'rpc' but expect and check also the NETCONF\n"
" envelopes <rpc> or <action>.\n"
" reply - Reply to a YANG RPC/action, defined as output. Note that\n"
" the reply data are expected inside a container representing\n"
" the original RPC/action invocation.\n"
" nc-reply - Similar to 'reply' but expect and check also the NETCONF\n"
" envelope <rpc-reply> with output data nodes as direct\n"
" descendants. The original RPC/action invocation is expected\n"
" in a separate parameter '-R' and is parsed as 'nc-rpc'.\n"
" notif - Notification instance of a YANG notification.\n"
" nc-notif - Similar to 'notif' but expect and check also the NETCONF\n"
" envelope <notification> with element <eventTime> and its\n"
" sibling as the actual notification.\n\n");
printf(" -d MODE, --default=MODE\n"
" Print data with default values, according to the MODE\n"
" (to print attributes, ietf-netconf-with-defaults model\n"
" must be loaded):\n"
" all - Add missing default nodes.\n"
" all-tagged - Add missing default nodes and mark all the default\n"
" nodes with the attribute.\n"
" trim - Remove all nodes with a default value.\n"
" implicit-tagged - Add missing nodes and mark them with the attribute.\n\n");
printf(" -E XPATH, --data-xpath=XPATH\n"
" Evaluate XPATH expression over the data and print the nodes satisfying\n"
" the expression. The output format is specific and the option cannot\n"
" be combined with the -f and -d options. Also all the data\n"
" inputs are merged into a single data tree where the expression\n"
" is evaluated, so the -m option is always set implicitly.\n\n");
printf(" -l, --list Print info about the loaded schemas.\n"
" (i - imported module, I - implemented module)\n"
" In case the '-f' option with data encoding is specified,\n"
" the list is printed as \"ietf-yang-library\" data.\n\n");
printf(" -L LINE_LENGTH, --tree-line-length=LINE_LENGTH\n"
" The limit of the maximum line length on which the 'tree'\n"
" format will try to be printed.\n\n");
printf(" -o OUTFILE, --output=OUTFILE\n"
" Write the output to OUTFILE instead of stdout.\n\n");
printf(" -O FILE, --operational=FILE\n"
" Provide optional data to extend validation of the '(nc-)rpc',\n"
" '(nc-)reply' or '(nc-)notif' TYPEs. The FILE is supposed to contain\n"
" the operational datastore referenced from the operation.\n"
" In case of a nested notification or action, its parent existence\n"
" is also checked in these operational data.\n\n");
printf(" -R FILE, --reply-rpc=FILE\n"
" Provide source RPC for parsing of the 'nc-reply' TYPE. The FILE\n"
" is supposed to contain the source 'nc-rpc' operation of the reply.\n\n");
printf(" -m, --merge Merge input data files into a single tree and validate at\n"
" once. The option has effect only for 'data' and 'config' TYPEs.\n\n");
printf(" -y, --yang-library\n"
" Load and implement internal \"ietf-yang-library\" YANG module.\n"
" Note that this module includes definitions of mandatory state\n"
" data that can result in unexpected data validation errors.\n\n");
printf(" -Y FILE, --yang-library-file=FILE\n"
" Parse FILE with \"ietf-yang-library\" data and use them to\n"
" create an exact YANG schema context. If specified, the '-F'\n"
" parameter (enabled features) is ignored.\n\n");
printf(" -X, --extended-leafref\n"
" Allow usage of deref() XPath function within leafref\n\n");
printf(" -J, --json-null\n"
" Allow usage of JSON empty values ('null') within input data\n\n");
printf(" -G GROUPS, --debug=GROUPS\n"
#ifndef NDEBUG
" Enable printing of specific debugging message group\n"
" (nothing will be printed unless verbosity is set to debug):\n"
" <group>[,<group>]* (dict, xpath, dep-sets)\n\n"
#else
" Unsupported for the Release build\n\n"
#endif
);
}
static void
libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *data_path, const char *schema_path, uint64_t line)
{
char *levstr;
switch (level) {
case LY_LLERR:
levstr = "err :";
break;
case LY_LLWRN:
levstr = "warn:";
break;
case LY_LLVRB:
levstr = "verb:";
break;
default:
levstr = "dbg :";
break;
}
if (data_path) {
fprintf(stderr, "libyang %s %s (%s)\n", levstr, msg, data_path);
} else if (schema_path) {
fprintf(stderr, "libyang %s %s (%s)\n", levstr, msg, schema_path);
} else if (line) {
fprintf(stderr, "libyang %s %s (line %" PRIu64 ")\n", levstr, msg, line);
} else {
fprintf(stderr, "libyang %s %s\n", levstr, msg);
}
}
static struct yl_schema_features *
get_features_not_applied(const struct ly_set *fset)
{
for (uint32_t u = 0; u < fset->count; ++u) {
struct yl_schema_features *sf = fset->objs[u];
if (!sf->applied) {
return sf;
}
}
return NULL;
}
/**
* @brief Create the libyang context.
*
* @param[in] yang_lib_file Context can be defined in yang library file.
* @param[in] searchpaths Directories in which modules are searched.
* @param[in,out] schema_features Set of features.
* @param[in,out] ctx_options Options for libyang context.
* @param[out] ctx Context for libyang.
* @return 0 on success.
*/
static int
create_ly_context(const char *yang_lib_file, const char *searchpaths, struct ly_set *schema_features,
uint16_t *ctx_options, struct ly_ctx **ctx)
{
if (yang_lib_file) {
/* ignore features */
ly_set_erase(schema_features, yl_schema_features_free);
if (ly_ctx_new_ylpath(searchpaths, yang_lib_file, LYD_UNKNOWN, *ctx_options, ctx)) {
YLMSG_E("Unable to modify libyang context with yang-library data.");
return -1;
}
} else {
/* set imp feature flag if all should be enabled */
(*ctx_options) |= !schema_features->count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
if (ly_ctx_new(searchpaths, *ctx_options, ctx)) {
YLMSG_E("Unable to create libyang context.");
return -1;
}
}
return 0;
}
/**
* @brief Implement module if some feature has not been applied.
*
* @param[in] schema_features Set of features.
* @param[in,out] ctx Context for libyang.
* @return 0 on success.
*/
static int
apply_features(struct ly_set *schema_features, struct ly_ctx *ctx)
{
struct yl_schema_features *sf;
struct lys_module *mod;
/* check that all specified features were applied, apply now if possible */
while ((sf = get_features_not_applied(schema_features))) {
/* try to find implemented or the latest revision of this module */
mod = ly_ctx_get_module_implemented(ctx, sf->mod_name);
if (!mod) {
mod = ly_ctx_get_module_latest(ctx, sf->mod_name);
}
if (!mod) {
YLMSG_E("Specified features not applied, module \"%s\" not loaded.", sf->mod_name);
return 1;
}
/* we have the module, implement it if needed and enable the specific features */
if (lys_set_implemented(mod, (const char **)sf->features)) {
YLMSG_E("Implementing module \"%s\" failed.", mod->name);
return 1;
}
sf->applied = 1;
}
return 0;
}
/**
* @brief Parse and compile modules, data are only stored for later processing.
*
* @param[in] argc Number of strings in @p argv.
* @param[in] argv Strings from command line.
* @param[in] optind Index to the first input file in @p argv.
* @param[in] data_in_format Specified input data format.
* @param[in,out] ctx Context for libyang.
* @param[in,out] yo Options for yanglint.
* @return 0 on success.
*/
static int
fill_context_inputs(int argc, char *argv[], int optind, LYD_FORMAT data_in_format, struct ly_ctx *ctx,
struct yl_opt *yo)
{
char *filepath = NULL;
LYS_INFORMAT format_schema;
LYD_FORMAT format_data;
for (int i = 0; i < argc - optind; i++) {
format_schema = LYS_IN_UNKNOWN;
format_data = data_in_format;
filepath = argv[optind + i];
if (!filepath) {
return -1;
}
if (get_format(filepath, &format_schema, &format_data)) {
return -1;
}
if (format_schema) {
if (cmd_add_exec(&ctx, yo, filepath)) {
return -1;
}
} else {
if (cmd_data_store(&ctx, yo, filepath)) {
return -1;
}
}
}
/* Check that all specified features were applied, apply now if possible. */
if (apply_features(&yo->schema_features, ctx)) {
return -1;
}
return 0;
}
/**
* @brief Enable specific debugging messages.
*
* @param[in] groups String in the form "<group>[,group>]*".
* @param[in,out] yo Options for yanglint.
* return 0 on success.
*/
static int
set_debug_groups(char *groups, struct yl_opt *yo)
{
int rc;
char *str, *end;
/* Process all debug arguments except the last one. */
for (str = groups; (end = strchr(str, ',')); str = end + 1) {
/* Temporary modify input string. */
*end = '\0';
rc = cmd_debug_store(NULL, yo, str);
*end = ',';
if (rc) {
return -1;
}
}
/* Process single/last debug argument. */
if (cmd_debug_store(NULL, yo, str)) {
return -1;
}
/* All debug arguments are valid, so they can apply. */
if (cmd_debug_setlog(NULL, yo)) {
return -1;
}
return 0;
}
/**
* @brief Process command line options and store the settings into the context.
*
* return -1 in case of error;
* return 0 in case of success and ready to process
* return 1 in case of success, but expect to exit.
*/
static int
fill_context(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
{
int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"verbose", no_argument, NULL, 'V'},
{"quiet", no_argument, NULL, 'Q'},
{"format", required_argument, NULL, 'f'},
{"path", required_argument, NULL, 'p'},
{"disable-searchdir", no_argument, NULL, 'D'},
{"features", required_argument, NULL, 'F'},
{"make-implemented", no_argument, NULL, 'i'},
{"in-format", required_argument, NULL, 'I'},
{"schema-node", required_argument, NULL, 'P'},
{"single-node", no_argument, NULL, 'q'},
{"submodule", required_argument, NULL, 's'},
{"ext-data", required_argument, NULL, 'x'},
{"not-strict", no_argument, NULL, 'n'},
{"present", no_argument, NULL, 'e'},
{"type", required_argument, NULL, 't'},
{"default", required_argument, NULL, 'd'},
{"data-xpath", required_argument, NULL, 'E'},
{"list", no_argument, NULL, 'l'},
{"tree-line-length", required_argument, NULL, 'L'},
{"output", required_argument, NULL, 'o'},
{"operational", required_argument, NULL, 'O'},
{"reply-rpc", required_argument, NULL, 'R'},
{"merge", no_argument, NULL, 'm'},
{"yang-library", no_argument, NULL, 'y'},
{"yang-library-file", required_argument, NULL, 'Y'},
{"extended-leafref", no_argument, NULL, 'X'},
{"json-null", no_argument, NULL, 'J'},
{"debug", required_argument, NULL, 'G'},
{NULL, 0, NULL, 0}
};
uint8_t data_type_set = 0;
yo->ctx_options = YL_DEFAULT_CTX_OPTIONS;
yo->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
yo->data_validate_options = YL_DEFAULT_DATA_VALIDATE_OPTIONS;
yo->line_length = 0;
opterr = 0;
while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:XJx:G:", options, &opt_index)) != -1) {
switch (opt) {
case 'h': /* --help */
help(0);
return 1;
case 'v': /* --version */
version();
return 1;
case 'V': { /* --verbose */
LY_LOG_LEVEL verbosity = ly_log_level(LY_LLERR);
if (verbosity < LY_LLDBG) {
++verbosity;
}
ly_log_level(verbosity);
break;
} /* case 'V' */
case 'Q': { /* --quiet */
LY_LOG_LEVEL verbosity = ly_log_level(LY_LLERR);
if (verbosity == LY_LLERR) {
/* turn logging off */
ly_log_options(LY_LOSTORE_LAST);
} else if (verbosity > LY_LLERR) {
--verbosity;
}
ly_log_level(verbosity);
break;
} /* case 'Q' */
case 'f': /* --format */
if (yl_opt_update_out_format(optarg, yo)) {
help(1);
return -1;
}
break;
case 'I': /* --in-format */
if (yo_opt_update_data_in_format(optarg, yo)) {
YLMSG_E("Unknown input format %s.", optarg);
help(1);
return -1;
}
break;
case 'p': /* --path */
if (searchpath_strcat(&yo->searchpaths, optarg)) {
YLMSG_E("Storing searchpath failed.");
return -1;
}
break;
/* case 'p' */
case 'D': /* --disable-searchdir */
if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
YLMSG_W("The -D option specified too many times.");
}
yo_opt_update_disable_searchdir(yo);
break;
case 'F': /* --features */
if (parse_features(optarg, &yo->schema_features)) {
return -1;
}
break;
case 'i': /* --make-implemented */
yo_opt_update_make_implemented(yo);
break;
case 'P': /* --schema-node */
yo->schema_node_path = optarg;
break;
case 'q': /* --single-node */
yo->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
break;
case 's': /* --submodule */
yo->submodule = optarg;
break;
case 'x': /* --ext-data */
yo->schema_context_filename = optarg;
break;
case 'n': /* --not-strict */
yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
case 'e': /* --present */
yo->data_validate_options |= LYD_VALIDATE_PRESENT;
break;
case 't': /* --type */
if (data_type_set) {
YLMSG_E("The data type (-t) cannot be set multiple times.");
return -1;
}
if (yl_opt_update_data_type(optarg, yo)) {
YLMSG_E("Unknown data tree type %s.", optarg);
help(1);
return -1;
}
data_type_set = 1;
break;
case 'd': /* --default */
if (yo_opt_update_data_default(optarg, yo)) {
YLMSG_E("Unknown default mode %s.", optarg);
help(1);
return -1;
}
break;
case 'E': /* --data-xpath */
if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
YLMSG_E("Storing XPath \"%s\" failed.", optarg);
return -1;
}
break;
case 'l': /* --list */
yo->list = 1;
break;
case 'L': /* --tree-line-length */
yo->line_length = atoi(optarg);
break;
case 'o': /* --output */
if (yo->out) {
YLMSG_E("Only a single output can be specified.");
return -1;
} else {
if (ly_out_new_filepath(optarg, &yo->out)) {
YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
return -1;
}
}
break;
case 'O': /* --operational */
if (yo->data_operational.path) {
YLMSG_E("The operational datastore (-O) cannot be set multiple times.");
return -1;
}
yo->data_operational.path = optarg;
break;
case 'R': /* --reply-rpc */
if (yo->reply_rpc.path) {
YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.");
return -1;
}
yo->reply_rpc.path = optarg;
break;
case 'm': /* --merge */
yo->data_merge = 1;
break;
case 'y': /* --yang-library */
yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
break;
case 'Y': /* --yang-library-file */
yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
yo->yang_lib_file = optarg;
break;
case 'X': /* --extended-leafref */
yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
break;
case 'J': /* --json-null */
yo->data_parse_options |= LYD_PARSE_JSON_NULL;
break;
case 'G': /* --debug */
if (set_debug_groups(optarg, yo)) {
return -1;
}
break;
default:
YLMSG_E("Invalid option or missing argument: -%c.", optopt);
return -1;
} /* switch */
}
/* additional checks for the options combinations */
if (!yo->list && (optind >= argc)) {
help(1);
YLMSG_E("Missing <schema> to process.");
return 1;
}
if (cmd_data_dep(yo, 0)) {
return -1;
}
if (cmd_print_dep(yo, 0)) {
return -1;
}
/* Create the libyang context. */
if (create_ly_context(yo->yang_lib_file, yo->searchpaths, &yo->schema_features, &yo->ctx_options, ctx)) {
return -1;
}
/* Set callback providing run-time extension instance data. */
if (yo->schema_context_filename) {
ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, yo->schema_context_filename);
}
/* Schema modules and data files are just checked and prepared into internal structures for further processing. */
if (fill_context_inputs(argc, argv, optind, yo->data_in_format, *ctx, yo)) {
return -1;
}
/* the second batch of checks */
if (yo->schema_print_options && !yo->schema_out_format) {
YLMSG_W("Schema printer options specified, but the schema output format is missing.");
}
if (yo->schema_parse_options && !yo->schema_modules.count) {
YLMSG_W("Schema parser options specified, but no schema input file provided.");
}
if (yo->data_print_options && !yo->data_out_format) {
YLMSG_W("data printer options specified, but the data output format is missing.");
}
if (((yo->data_parse_options != YL_DEFAULT_DATA_PARSE_OPTIONS) || yo->data_type) && !yo->data_inputs.count) {
YLMSG_W("Data parser options specified, but no data input file provided.");
}
return 0;
}
int
main_ni(int argc, char *argv[])
{
int ret = EXIT_SUCCESS, r;
struct yl_opt yo = {0};
struct ly_ctx *ctx = NULL;
char *features_output = NULL;
struct ly_set set = {0};
uint32_t u;
/* set callback for printing libyang messages */
ly_set_log_clb(libyang_verbclb);
r = fill_context(argc, argv, &yo, &ctx);
if (r < 0) {
ret = EXIT_FAILURE;
}
if (r) {
goto cleanup;
}
/* do the required job - parse, validate, print */
if (yo.list) {
/* print the list of schemas */
ret = cmd_list_exec(&ctx, &yo, NULL);
goto cleanup;
}
if (yo.feature_param_format) {
for (u = 0; u < yo.schema_modules.count; u++) {
if ((ret = cmd_feature_exec(&ctx, &yo, ((struct lys_module *)yo.schema_modules.objs[u])->name))) {
goto cleanup;
}
}
cmd_feature_fin(ctx, &yo);
} else if (yo.schema_out_format && yo.schema_node_path) {
if ((ret = cmd_print_exec(&ctx, &yo, NULL))) {
goto cleanup;
}
} else if (yo.schema_out_format && yo.submodule) {
if ((ret = cmd_print_exec(&ctx, &yo, yo.submodule))) {
goto cleanup;
}
} else if (yo.schema_out_format) {
for (u = 0; u < yo.schema_modules.count; ++u) {
yo.last_one = (u + 1) == yo.schema_modules.count;
if ((ret = cmd_print_exec(&ctx, &yo, ((struct lys_module *)yo.schema_modules.objs[u])->name))) {
goto cleanup;
}
}
}
/* do the data validation despite the schema was printed */
if (yo.data_inputs.size) {
if ((ret = cmd_data_process(ctx, &yo))) {
goto cleanup;
}
}
cleanup:
/* cleanup */
yl_opt_erase(&yo);
ly_ctx_destroy(ctx);
free(features_output);
ly_set_erase(&set, NULL);
return ret;
}

22
tools/lint/main_ni_only.c Normal file
View file

@ -0,0 +1,22 @@
/**
* @file main_ni_only.c
* @brief non-interactive implementation of main() for those platforms without the linenoise library
*
* Copyright (c) 2015-2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
int main_ni(int argc, char *argv[]);
int done; /* for cmd.c */
int
main(int argc, char *argv[])
{
return main_ni(argc, argv);
}

136
tools/lint/yanglint.1 Normal file
View file

@ -0,0 +1,136 @@
.\" Manpage for yanglint.
.\" Process this file with
.\" groff -man -Tascii yanglint.1
.\"
.TH YANGLINT 1 "2016-10-27" "libyang"
.SH NAME
yanglint \- YANG lint tool
.
.SH SYNOPSIS
.B yanglint
.br
.B yanglint
[\fIOPTIONS\fP]
[\-f { \fByang\fP | \fByin\fP | \fBtree\fP } ]
.I FILE ...
.br
.B yanglint
[\fIOPTIONS\fP]
[\-f { \fBxml\fP | \fBjson\fP } ]
\fISCHEMA\fP...
\fIFILE\fP...
.
.SH DESCRIPTION
\fByanglint\fP is a command-line tool for validating and converting YANG
schemas and the YANG modeled data. For a simple use, it validates the provided
file and if the output format specified, it converts input data into the output
format. If started with no argument, \fByanglint\fP opens interactive
environment where the user is allowed to work with schemas and data in a more
complex way.
.
.SH OPTIONS
.TP
.BR "\-h\fR,\fP \-\^\-help"
Outputs usage help and exits.
.TP
.BR "\-v\fR,\fP \-\^\-version"
Outputs the version number and exits.
.TP
.BR "\-V\fR,\fP \-\^\-verbose"
Increases the verbosity level. If not specified, only errors are printed, with
each appearance it adds: warnings, verbose messages, debug messages (if compiled
with debug information).
.TP
.BR "\-p \fIPATH\fP\fR,\fP \-\^\-path=\fIPATH\fP"
Specifies search path for getting imported modules or included submodules. The option
can be used multiple times. The current working directory and path of the module
being added is used implicitly.
.TP
.BR "\-s\fR,\fP \-\^\-strict"
Changes handling of unknown data nodes - instead of silently ignoring unknown data,
error is printed and data parsing fails. This option applies only on data parsing.
.TP
.BR "\-f \fIFORMAT\fP\fR,\fP \-\^\-format=\fIFORMAT\fP"
Converts the content of the input \fIFILE\fPs into the specified \fIFORMAT\fP. If no
\fIOUTFILE\fP is specified, the data are printed on the standard output. Only the
compatible formats for the input \fIFILE\fPs are allowed, see the section \fBFORMATS\fP.
.TP
.BR "\-o \fIOUTFILE\fP\fR,\fP \-\^\-output=\fIOUTFILE\fP"
Writes the output data into the specified \fIOUTFILE\fP. The option can be used
only in combination with \fB--format\fR option. In case of converting schema, only
a single input schema \fIFILE\fP is allowed. In case of data input \fIFILE\fPs,
input is merged and printed into a single \fIOUTFILE\fP.
.TP
.BR "\-F \fIFEATURES\fP\fR,\fP \-\^\-features=\fIFEATURES\fP"
Specifies the list of enabled features in the format
\fIMODULE\fP:[\fIFEATURE\fP,...]. In case of processing multiple modules, the
option can be used repeatedly. To disable all the features, use an empty list
specified for the particular module.
.TP
.BR "\-d \fIMODE\fP\fR,\fP \-\^\-default=\fIMODE\fP"
Print data with default values, according to the \fIMODE\fP (to print attributes,
the ietf-netconf-with-defaults model must be loaded). The \fIMODE\fP is one of the following:
\[bu] \fBall\fP - add missing default nodes
\[bu] \fBall-tagged\fP - add missing default nodes and mark all the default nodes with the attribute
\[bu] \fBtrim\fP - remove all nodes with a default value
\[bu] \fBimplicit-tagged\fP - add missing nodes and mark them with the attribute
.TP
.BR "\-t \fITYPE\fP\fR,\fP \-\^\-type=\fITYPE\fP"
Specify data tree type in the input data \fIFILE\fPs. The \fITYPE\fP is one of the following:
\[bu] \fBauto\fP - Resolve data type (one of the following) automatically (as pyang does). Applicable only on XML input data.
\[bu] \fBdata\fP - Complete datastore with status data (default type).
\[bu] \fBconfig\fP - Configuration datastore (without status data).
\[bu] \fBget\fP - Result of the NETCONF <get> operation.
\[bu] \fBgetconfig\fP - Result of the NETCONF <get-config> operation.
\[bu] \fBedit\fP - Content of the NETCONF <edit-config> operation.
\[bu] \fBrpc\fP - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.
\[bu] \fBrpcreply\fP - Reply to the RPC. This is just a virtual \fITYPE\fP, for parsing replies, '\fBauto\fP' must be used since the data \fIFILE\fPs are expected in pairs.
.br
The first input data \fIFILE\fP is expected as '\fBrpc\fP' \fITYPE\fP, the second \fIFILE\fP is expected as reply to the previous RPC.
\[bu] \fBnotif\fP - Notification instance (content of the <notification> element without <eventTime>.
.TP
.BR "\-O \fIFILE\fP\fR,\fP \-\^\-operational=\fIFILE\fP]
Optional parameter for '\fBrpc\fP' and '\fBnotif\fP' \fITYPE\fPs, the \fIFILE\fP contains running configuration datastore and
state data referenced from the RPC/Notification. The same data apply to all input data \fIFILE\fPs. Note that the file
is validated as '\fBdata\fP' \fITYPE\fP. Special value '\fB!\fP' can be used as \fIFILE\fP argument to ignore the external references.
.TP
.BR "\-y \fIYANGLIB_PATH\fP"
Specify path to a yang-library data file (XML or JSON) describing the initial context.
If provided, yanglint loads the modules according to the content of the yang-library data tree.
Otherwise, an empty content with only the internal libyang modules is used. This does
not limit user to load another modules explicitly specified as command line parameters.
.
.SH FORMATS
There are two types of formats to use.
.TP
.I Schemas
In case of schemas, the content can be converted into the '\fByang\fP', '\fByin\fP'
and '\fBtree\fP' formats. As input, only YANG and YIN files are
accepted. Note, that the corresponding file extension is required.
.TP
.I Data\ \ \
In case of YANG modeled data, the content can be converted between '\fBxml\fP'
and '\fBjson\fP' formats. Remember that the corresponding file extension of the
input file is required.
.
.SH EXAMPLES
.IP \[bu] 2
Open interactive environment:
yanglint
.IP \[bu]
Convert YANG model into YIN and print it to the stdout:
yanglint --format=yin ./ietf-system.yang
.IP \[bu]
Convert ietf-system configuration data from XML to JSON:
yanglint --format=json --type=config --output=data.json ./ietf-system.yang ./data.xml
.SH SEE ALSO
https://github.com/CESNET/libyang (libyang homepage and Git repository)
.
.SH AUTHORS
Radek Krejci <rkrejci@cesnet.cz>, Michal Vasko <mvasko@cesnet.cz>
.
.SH COPYRIGHT
Copyright \(co 2015-2017 CESNET, a.l.e.

344
tools/lint/yl_opt.c Normal file
View file

@ -0,0 +1,344 @@
/**
* @file yl_opt.c
* @author Adam Piecek <piecek@cesnet.cz>
* @brief Settings options for the libyang context.
*
* Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <strings.h>
#include "in.h" /* ly_in_free */
#include "common.h"
#include "yl_opt.h"
#include "yl_schema_features.h"
struct cmdline_file *
fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
{
struct cmdline_file *rec;
rec = malloc(sizeof *rec);
if (!rec) {
YLMSG_E("Allocating memory for data file information failed.");
return NULL;
}
rec->in = in;
rec->path = path;
rec->format = format;
if (set && ly_set_add(set, rec, 1, NULL)) {
free(rec);
YLMSG_E("Storing data file information failed.");
return NULL;
}
return rec;
}
void
free_cmdline_file_items(struct cmdline_file *rec)
{
if (rec && rec->in) {
ly_in_free(rec->in, 1);
}
}
void
free_cmdline_file(void *cmdline_file)
{
struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
if (rec) {
free_cmdline_file_items(rec);
free(rec);
}
}
void
yl_opt_erase(struct yl_opt *yo)
{
ly_bool interactive;
interactive = yo->interactive;
/* data */
ly_set_erase(&yo->data_inputs, free_cmdline_file);
ly_in_free(yo->data_operational.in, 1);
ly_set_erase(&yo->data_xpath, NULL);
/* schema */
ly_set_erase(&yo->schema_features, yl_schema_features_free);
ly_set_erase(&yo->schema_modules, NULL);
/* context */
free(yo->searchpaths);
/* --reply-rpc */
ly_in_free(yo->reply_rpc.in, 1);
ly_out_free(yo->out, NULL, yo->out_stdout ? 0 : 1);
free_cmdline(yo->argv);
*yo = (const struct yl_opt) {
0
};
yo->interactive = interactive;
}
int
yl_opt_update_schema_out_format(const char *arg, struct yl_opt *yo)
{
if (!strcasecmp(arg, "yang")) {
yo->schema_out_format = LYS_OUT_YANG;
yo->data_out_format = 0;
} else if (!strcasecmp(arg, "yin")) {
yo->schema_out_format = LYS_OUT_YIN;
yo->data_out_format = 0;
} else if (!strcasecmp(arg, "info")) {
yo->schema_out_format = LYS_OUT_YANG_COMPILED;
yo->data_out_format = 0;
} else if (!strcasecmp(arg, "tree")) {
yo->schema_out_format = LYS_OUT_TREE;
yo->data_out_format = 0;
} else {
return 1;
}
return 0;
}
int
yl_opt_update_data_out_format(const char *arg, struct yl_opt *yo)
{
if (!strcasecmp(arg, "xml")) {
yo->schema_out_format = 0;
yo->data_out_format = LYD_XML;
} else if (!strcasecmp(arg, "json")) {
yo->schema_out_format = 0;
yo->data_out_format = LYD_JSON;
} else if (!strcasecmp(arg, "lyb")) {
yo->schema_out_format = 0;
yo->data_out_format = LYD_LYB;
} else {
return 1;
}
return 0;
}
static int
yl_opt_update_other_out_format(const char *arg, struct yl_opt *yo)
{
if (!strcasecmp(arg, "feature-param")) {
yo->feature_param_format = 1;
} else {
return 1;
}
return 0;
}
int
yl_opt_update_out_format(const char *arg, struct yl_opt *yo)
{
if (!yl_opt_update_schema_out_format(arg, yo)) {
return 0;
}
if (!yl_opt_update_data_out_format(arg, yo)) {
return 0;
}
if (!yl_opt_update_other_out_format(arg, yo)) {
return 0;
}
YLMSG_E("Unknown output format %s.", arg);
return 1;
}
int
yl_opt_update_data_type(const char *arg, struct yl_opt *yo)
{
if (!strcasecmp(arg, "config")) {
yo->data_parse_options |= LYD_PARSE_NO_STATE;
yo->data_validate_options |= LYD_VALIDATE_NO_STATE;
} else if (!strcasecmp(arg, "get")) {
yo->data_parse_options |= LYD_PARSE_ONLY;
} else if (!strcasecmp(arg, "getconfig") || !strcasecmp(arg, "get-config") || !strcasecmp(arg, "edit")) {
yo->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
} else if (!strcasecmp(arg, "rpc") || !strcasecmp(arg, "action")) {
yo->data_type = LYD_TYPE_RPC_YANG;
} else if (!strcasecmp(arg, "nc-rpc")) {
yo->data_type = LYD_TYPE_RPC_NETCONF;
} else if (!strcasecmp(arg, "reply") || !strcasecmp(arg, "rpcreply")) {
yo->data_type = LYD_TYPE_REPLY_YANG;
} else if (!strcasecmp(arg, "nc-reply")) {
yo->data_type = LYD_TYPE_REPLY_NETCONF;
} else if (!strcasecmp(arg, "notif") || !strcasecmp(arg, "notification")) {
yo->data_type = LYD_TYPE_NOTIF_YANG;
} else if (!strcasecmp(arg, "nc-notif")) {
yo->data_type = LYD_TYPE_NOTIF_NETCONF;
} else if (!strcasecmp(arg, "data")) {
/* default option */
} else {
return 1;
}
return 0;
}
int
yo_opt_update_data_default(const char *arg, struct yl_opt *yo)
{
if (!strcasecmp(arg, "all")) {
yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
} else if (!strcasecmp(arg, "all-tagged")) {
yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
} else if (!strcasecmp(arg, "trim")) {
yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
} else if (!strcasecmp(arg, "implicit-tagged")) {
yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
} else {
return 1;
}
return 0;
}
int
yo_opt_update_data_in_format(const char *arg, struct yl_opt *yo)
{
if (!strcasecmp(arg, "xml")) {
yo->data_in_format = LYD_XML;
} else if (!strcasecmp(arg, "json")) {
yo->data_in_format = LYD_JSON;
} else if (!strcasecmp(arg, "lyb")) {
yo->data_in_format = LYD_LYB;
} else {
return 1;
}
return 0;
}
void
yo_opt_update_make_implemented(struct yl_opt *yo)
{
if (yo->ctx_options & LY_CTX_REF_IMPLEMENTED) {
yo->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
yo->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
} else {
yo->ctx_options |= LY_CTX_REF_IMPLEMENTED;
}
}
void
yo_opt_update_disable_searchdir(struct yl_opt *yo)
{
if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIR_CWD) {
yo->ctx_options &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIRS;
} else {
yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
}
}
void
free_cmdline(char *argv[])
{
if (argv) {
free(argv[0]);
free(argv);
}
}
int
parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
{
int count;
char **vector;
char *ptr;
char qmark = 0;
assert(cmdline);
assert(argc_p);
assert(argv_p);
/* init */
optind = 0; /* reinitialize getopt() */
count = 1;
vector = malloc((count + 1) * sizeof *vector);
vector[0] = strdup(cmdline);
/* command name */
strtok(vector[0], " ");
/* arguments */
while ((ptr = strtok(NULL, " "))) {
size_t len;
void *r;
len = strlen(ptr);
if (qmark) {
/* still in quotated text */
/* remove NULL termination of the previous token since it is not a token,
* but a part of the quotation string */
ptr[-1] = ' ';
if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
/* end of quotation */
qmark = 0;
/* shorten the argument by the terminating quotation mark */
ptr[len - 1] = '\0';
}
continue;
}
/* another token in cmdline */
++count;
r = realloc(vector, (count + 1) * sizeof *vector);
if (!r) {
YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
free(vector);
return -1;
}
vector = r;
vector[count - 1] = ptr;
if ((ptr[0] == '"') || (ptr[0] == '\'')) {
/* remember the quotation mark to identify end of quotation */
qmark = ptr[0];
/* move the remembered argument after the quotation mark */
++vector[count - 1];
/* check if the quotation is terminated within this token */
if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
/* end of quotation */
qmark = 0;
/* shorten the argument by the terminating quotation mark */
ptr[len - 1] = '\0';
}
}
}
vector[count] = NULL;
*argc_p = count;
*argv_p = vector;
return 0;
}

237
tools/lint/yl_opt.h Normal file
View file

@ -0,0 +1,237 @@
/**
* @file yl_opt.h
* @author Adam Piecek <piecek@cesnet.cz>
* @brief Settings options for the libyang context.
*
* Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef YL_OPT_H_
#define YL_OPT_H_
#include "parser_data.h" /* enum lyd_type */
#include "printer_schema.h" /* LYS_OUTFORMAT */
#include "set.h" /* ly_set */
/**
* @brief Data connected with a file provided on a command line as a file path.
*/
struct cmdline_file {
struct ly_in *in;
const char *path;
LYD_FORMAT format;
};
/**
* @brief Create and fill the command line file data (struct cmdline_file *).
* @param[in] set Optional parameter in case the record is supposed to be added into a set.
* @param[in] in Input file handler.
* @param[in] path Filepath of the file.
* @param[in] format Format of the data file.
* @return The created command line file structure.
* @return NULL on failure
*/
struct cmdline_file *fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format);
/**
* @brief Free the command line file data items.
* @param[in,out] rec record to free.
*/
void free_cmdline_file_items(struct cmdline_file *rec);
/**
* @brief Free the command line file data (struct cmdline_file *).
* @param[in,out] cmdline_file The (struct cmdline_file *) to free.
*/
void free_cmdline_file(void *cmdline_file);
/**
* @brief Context structure to hold and pass variables in a structured form.
*/
struct yl_opt {
/* Set to 1 if yanglint running in the interactive mode */
ly_bool interactive;
ly_bool last_one;
/* libyang context for the run */
char *yang_lib_file;
uint16_t ctx_options;
/* prepared output (--output option or stdout by default) */
ly_bool out_stdout;
struct ly_out *out;
char *searchpaths;
ly_bool searchdir_unset;
/* options flags */
uint8_t list; /* -l option to print list of schemas */
/* line length for 'tree' format */
size_t line_length; /* --tree-line-length */
uint32_t dbg_groups;
/*
* schema
*/
/* set schema modules' features via --features option (struct schema_features *) */
struct ly_set schema_features;
/* set of loaded schema modules (struct lys_module *) */
struct ly_set schema_modules;
/* options to parse and print schema modules */
uint32_t schema_parse_options;
uint32_t schema_print_options;
/* specification of printing schema node subtree, option --schema-node */
char *schema_node_path;
char *submodule;
/* name of file containing explicit context passed to callback
* for schema-mount extension. This also causes a callback to
* be registered.
*/
char *schema_context_filename;
ly_bool extdata_unset;
/* value of --format in case of schema format */
LYS_OUTFORMAT schema_out_format;
ly_bool feature_param_format;
ly_bool feature_print_all;
/*
* data
*/
/* various options based on --type option */
enum lyd_type data_type;
uint32_t data_parse_options;
uint32_t data_validate_options;
uint32_t data_print_options;
/* flag for --merge option */
uint8_t data_merge;
/* value of --format in case of data format */
LYD_FORMAT data_out_format;
/* value of --in-format in case of data format */
LYD_FORMAT data_in_format;
/* input data files (struct cmdline_file *) */
struct ly_set data_inputs;
/* storage for --operational */
struct cmdline_file data_operational;
/* storage for --reply-rpc */
struct cmdline_file reply_rpc;
/* storage for --data-xpath */
struct ly_set data_xpath;
char **argv;
};
/**
* @brief Erase all values in @p opt.
*
* The yl_opt.interactive item is not deleted.
*
* @param[in,out] yo Option context to erase.
*/
void yl_opt_erase(struct yl_opt *yo);
/**
* @brief Update @p yo according to the @p arg of the schema --format parameter.
*
* @param[in] arg Format parameter argument (for example yang, yin, ...).
* @param[out] yo yanglint options used to update.
* @return 0 on success.
*/
int yl_opt_update_schema_out_format(const char *arg, struct yl_opt *yo);
/**
* @brief Update @p yo according to the @p arg of the data --format parameter.
*
* @param[in] arg Format parameter argument (for example xml, json, ...).
* @param[out] yo yanglint options used to update.
* @return 0 on success.
*/
int yl_opt_update_data_out_format(const char *arg, struct yl_opt *yo);
/**
* @brief Update @p yo according to the @p arg of the general --format parameter.
*
* @param[in] arg Format parameter argument (for example yang, xml, ...).
* @param[out] yo yanglint options used to update.
* @return 0 on success.
*/
int yl_opt_update_out_format(const char *arg, struct yl_opt *yo);
/**
* @brief Update @p yo according to the @p arg of the data --type parameter.
*
* @param[in] arg Format parameter argument (for example config, rpc, ...).
* @param[out] yo yanglint options used to update.
* @return 0 on success.
*/
int yl_opt_update_data_type(const char *arg, struct yl_opt *yo);
/**
* @brief Update @p yo according to the @p arg of the data --default parameter.
*
* @param[in] arg Format parameter argument (for example all, trim, ...).
* @param[out] yo yanglint options used to update.
* @return 0 on success.
*/
int yo_opt_update_data_default(const char *arg, struct yl_opt *yo);
/**
* @brief Update @p yo according to the @p arg of the data --in-format parameter.
*
* @param[in] arg Format parameter argument (for example xml, json, ...).
* @param[out] yo yanglint options used to update.
* @return 0 on success.
*/
int yo_opt_update_data_in_format(const char *arg, struct yl_opt *yo);
/**
* @brief Update @p yo according to the --make-implemented parameter.
*
* @param[in,out] yo yanglint options used to update.
*/
void yo_opt_update_make_implemented(struct yl_opt *yo);
/**
* @brief Update @p yo according to the --disable-searchdir parameter.
*
* @param[in,out] yo yanglint options used to update.
*/
void yo_opt_update_disable_searchdir(struct yl_opt *yo);
/**
* @brief Helper function to prepare argc, argv pair from a command line string.
*
* @param[in] cmdline Complete command line string.
* @param[out] argc_p Pointer to store argc value.
* @param[out] argv_p Pointer to store argv vector.
* @return 0 on success, non-zero on failure.
*/
int parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[]);
/**
* @brief Destructor for the argument vector prepared by ::parse_cmdline().
*
* @param[in,out] argv Argument vector to destroy.
*/
void free_cmdline(char *argv[]);
#endif /* YL_OPT_H_ */

View file

@ -0,0 +1,212 @@
/**
* @file yl_schema_features.c
* @author Adam Piecek <piecek@cesnet.cz>
* @brief Control features for the schema.
*
* Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdlib.h> /* calloc */
#include <string.h> /* strcmp */
#include "compat.h" /* strndup */
#include "set.h" /* ly_set */
#include "common.h"
#include "yl_schema_features.h"
void
yl_schema_features_free(void *flist)
{
struct yl_schema_features *rec = (struct yl_schema_features *)flist;
if (rec) {
free(rec->mod_name);
if (rec->features) {
for (uint32_t u = 0; rec->features[u]; ++u) {
free(rec->features[u]);
}
free(rec->features);
}
free(rec);
}
}
void
get_features(const struct ly_set *fset, const char *module, const char ***features)
{
/* get features list for this module */
for (uint32_t u = 0; u < fset->count; ++u) {
struct yl_schema_features *sf = (struct yl_schema_features *)fset->objs[u];
if (!strcmp(module, sf->mod_name)) {
/* matched module - explicitly set features */
*features = (const char **)sf->features;
sf->applied = 1;
return;
}
}
/* features not set so disable all */
*features = NULL;
}
int
parse_features(const char *fstring, struct ly_set *fset)
{
struct yl_schema_features *rec = NULL;
uint32_t count;
char *p, **fp;
rec = calloc(1, sizeof *rec);
if (!rec) {
YLMSG_E("Unable to allocate features information record (%s).", strerror(errno));
goto error;
}
/* fill the record */
p = strchr(fstring, ':');
if (!p) {
YLMSG_E("Invalid format of the features specification (%s).", fstring);
goto error;
}
rec->mod_name = strndup(fstring, p - fstring);
count = 0;
while (p) {
size_t len = 0;
char *token = p + 1;
p = strchr(token, ',');
if (!p) {
/* the last item, if any */
len = strlen(token);
} else {
len = p - token;
}
if (len) {
fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
if (!fp) {
YLMSG_E("Unable to store features list information (%s).", strerror(errno));
goto error;
}
rec->features = fp;
fp = &rec->features[count++]; /* array item to set */
(*fp) = strndup(token, len);
}
}
/* terminating NULL */
fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
if (!fp) {
YLMSG_E("Unable to store features list information (%s).", strerror(errno));
goto error;
}
rec->features = fp;
rec->features[count++] = NULL;
/* Store record to the output set. */
if (ly_set_add(fset, rec, 1, NULL)) {
YLMSG_E("Unable to store features information (%s).", strerror(errno));
goto error;
}
rec = NULL;
return 0;
error:
yl_schema_features_free(rec);
return -1;
}
void
print_features(struct ly_out *out, const struct lys_module *mod)
{
struct lysp_feature *f;
uint32_t idx;
size_t max_len, len;
ly_print(out, "%s:\n", mod->name);
/* get max len, so the statuses of all the features will be aligned */
max_len = 0, idx = 0, f = NULL;
while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
len = strlen(f->name);
max_len = (max_len > len) ? max_len : len;
}
if (!max_len) {
ly_print(out, "\t(none)\n");
return;
}
/* print features */
idx = 0, f = NULL;
while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
ly_print(out, "\t%-*s (%s)\n", (int)max_len, f->name, lys_feature_value(mod, f->name) ? "off" : "on");
}
}
void
print_feature_param(struct ly_out *out, const struct lys_module *mod)
{
struct lysp_feature *f = NULL;
uint32_t idx = 0;
uint8_t first = 1;
ly_print(out, " -F %s:", mod->name);
while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
if (first) {
ly_print(out, "%s", f->name);
first = 0;
} else {
ly_print(out, ",%s", f->name);
}
}
}
void
print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t feature_param)
{
uint32_t i;
struct lys_module *mod;
uint8_t first;
/* Print features for all implemented modules. */
first = 1;
i = 0;
while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
if (!mod->implemented) {
continue;
}
if (first) {
print_features(out, mod);
first = 0;
} else {
ly_print(out, "\n");
print_features(out, mod);
}
}
if (!feature_param) {
return;
}
ly_print(out, "\n");
/* Print features for all implemented modules in 'feature-param' format. */
i = 0;
while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
if (mod->implemented) {
print_feature_param(out, mod);
}
}
}

View file

@ -0,0 +1,84 @@
/**
* @file yl_schema_features.h
* @author Adam Piecek <piecek@cesnet.cz>
* @brief Control features for the schema.
*
* Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef YL_SCHEMA_FEATURES_H_
#define YL_SCHEMA_FEATURES_H_
#include <stdint.h>
struct ly_set;
struct lys_module;
struct ly_out;
struct ly_ctx;
/**
* @brief Storage for the list of the features (their names) in a specific YANG module.
*/
struct yl_schema_features {
char *mod_name;
char **features;
uint8_t applied;
};
/**
* @brief Free the schema features list (struct schema_features *)
* @param[in,out] flist The (struct schema_features *) to free.
*/
void yl_schema_features_free(void *flist);
/**
* @brief Get the list of features connected with the specific YANG module.
*
* @param[in] fset The set of features information (struct schema_features *).
* @param[in] module Name of the YANG module which features should be found.
* @param[out] features Pointer to the list of features being returned.
*/
void get_features(const struct ly_set *fset, const char *module, const char ***features);
/**
* @brief Parse features being specified for the specific YANG module.
*
* Format of the input @p fstring is as follows: "<module_name>:[<feature>,]*"
*
* @param[in] fstring Input string to be parsed.
* @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
*/
int parse_features(const char *fstring, struct ly_set *fset);
/**
* @brief Print all features of a single module.
*
* @param[in] out The output handler for printing.
* @param[in] mod Module which can contains the features.
*/
void print_features(struct ly_out *out, const struct lys_module *mod);
/**
* @brief Print all features in the 'feature-param' format.
*
* @param[in] out The output handler for printing.
* @param[in] mod Module which can contains the features.
*/
void print_feature_param(struct ly_out *out, const struct lys_module *mod);
/**
* @brief Print all features of all implemented modules.
*
* @param[in] out The output handler for printing.
* @param[in] ctx Libyang context.
* @param[in] feature_param Flag expressing whether to print features parameter.
*/
void print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t feature_param);
#endif /* YL_SCHEMA_FEATURES_H_ */

20
tools/re/CMakeLists.txt Normal file
View file

@ -0,0 +1,20 @@
# yangre
set(resrc
main.c)
set(format_sources
${format_sources}
${CMAKE_CURRENT_SOURCE_DIR}/*.c
PARENT_SCOPE)
add_executable(yangre ${resrc} ${compatsrc})
target_link_libraries(yangre yang)
install(TARGETS yangre DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${PROJECT_SOURCE_DIR}/tools/re/yangre.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
target_include_directories(yangre BEFORE PRIVATE ${PROJECT_BINARY_DIR})
if(WIN32)
target_include_directories(yangre PRIVATE ${GETOPT_INCLUDE_DIR})
target_link_libraries(yangre ${GETOPT_LIBRARY})
endif()

461
tools/re/main.c Normal file
View file

@ -0,0 +1,461 @@
/**
* @file main.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's YANG Regular Expression tool
*
* Copyright (c) 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE /* asprintf, strdup */
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "libyang.h"
#include "compat.h"
#include "tools/config.h"
struct yr_pattern {
char *expr;
ly_bool invert;
};
void
help(void)
{
fprintf(stdout, "YANG Regular Expressions processor.\n");
fprintf(stdout, "Usage:\n");
fprintf(stdout, " yangre [-hv]\n");
fprintf(stdout, " yangre [-V] -p <regexp1> [-i] [-p <regexp2> [-i] ...] <string>\n");
fprintf(stdout, " yangre [-V] -f <file>\n");
fprintf(stdout, "Returns 0 if string matches the pattern(s) or if otherwise successful.\n");
fprintf(stdout, "Returns 1 on error.\n");
fprintf(stdout, "Returns 2 if string does not match the pattern(s).\n\n");
fprintf(stdout, "Options:\n"
" -h, --help Show this help message and exit.\n"
" -v, --version Show version number and exit.\n"
" -V, --verbose Print the processing information.\n"
" -i, --invert-match Invert-match modifier for the closest preceding\n"
" pattern.\n"
" -p, --pattern=\"REGEXP\" Regular expression including the quoting,\n"
" which is applied the same way as in a YANG module.\n"
" -f, --file=\"FILE\" List of patterns and the <string> (separated by an\n"
" empty line) are taken from <file>. Invert-match is\n"
" indicated by the single space character at the \n"
" beginning of the pattern line. YANG quotation around\n"
" patterns is still expected, but that avoids issues with\n"
" reading quotation by shell. Avoid newline at the end\n"
" of the string line to represent empty <string>.");
fprintf(stdout, "Examples:\n"
" pattern \"[0-9a-fA-F]*\"; -> yangre -p '\"[0-9a-fA-F]*\"' '1F'\n"
" pattern '[a-zA-Z0-9\\-_.]*'; -> yangre -p \"'[a-zA-Z0-9\\-_.]*'\" 'a-b'\n"
" pattern [xX][mM][lL].*; -> yangre -p '[xX][mM][lL].*' 'xml-encoding'\n\n");
fprintf(stdout, "Note that to pass YANG quoting through your shell, you are supposed to use\n"
"the other quotation around. For not-quoted patterns, use single quotes.\n\n");
}
void
version(void)
{
fprintf(stdout, "yangre %s\n", PROJECT_VERSION);
}
void
pattern_error(LY_LOG_LEVEL level, const char *msg, const char *UNUSED(data_path), const char *UNUSED(schema_path),
uint64_t UNUSED(line))
{
if (level == LY_LLERR) {
fprintf(stderr, "yangre error: %s\n", msg);
}
}
static int
add_pattern(struct yr_pattern **patterns, int *counter, char *pattern)
{
void *reallocated;
int orig_counter;
/* Store the original number of items. */
orig_counter = *counter;
/* Reallocate 'patterns' memory with additional space. */
reallocated = realloc(*patterns, (orig_counter + 1) * sizeof **patterns);
if (!reallocated) {
goto error;
}
(*patterns) = reallocated;
/* Allocated memory is now larger. */
(*counter)++;
/* Copy the pattern and store it to the additonal space. */
(*patterns)[orig_counter].expr = strdup(pattern);
if (!(*patterns)[orig_counter].expr) {
goto error;
}
(*patterns)[orig_counter].invert = 0;
return 0;
error:
fprintf(stderr, "yangre error: memory allocation error.\n");
return 1;
}
static int
create_empty_string(char **str)
{
free(*str);
*str = malloc(sizeof(char));
if (!(*str)) {
fprintf(stderr, "yangre error: memory allocation failed.\n");
return 1;
}
(*str)[0] = '\0';
return 0;
}
static ly_bool
file_is_empty(FILE *fp)
{
int c;
c = fgetc(fp);
if (c == EOF) {
return 1;
} else {
ungetc(c, fp);
return 0;
}
}
/**
* @brief Open the @p filepath, parse patterns and given string-argument.
*
* @param[in] filepath File to parse. Contains patterns and string.
* @param[out] infile The file descriptor of @p filepath.
* @param[out] patterns Storage of patterns.
* @param[out] patterns_count Number of items in @p patterns.
* @param[out] strarg The string-argument to check.
* @return 0 on success.
*/
static int
parse_patterns_file(const char *filepath, FILE **infile, struct yr_pattern **patterns, int *patterns_count, char **strarg)
{
int blankline = 0;
char *str = NULL;
size_t len = 0;
ssize_t l;
*infile = fopen(filepath, "rb");
if (!(*infile)) {
fprintf(stderr, "yangre error: unable to open input file %s (%s).\n", optarg, strerror(errno));
goto error;
}
if (file_is_empty(*infile)) {
if (create_empty_string(strarg)) {
goto error;
}
return 0;
}
while ((l = getline(&str, &len, *infile)) != -1) {
if (!blankline && ((str[0] == '\n') || ((str[0] == '\r') && (str[1] == '\n')))) {
/* blank line */
blankline = 1;
continue;
}
if ((str[0] != '\n') && (str[0] != '\r') && (str[l - 1] == '\n')) {
/* remove ending newline */
if ((l > 1) && (str[l - 2] == '\r') && (str[l - 1] == '\n')) {
str[l - 2] = '\0';
} else {
str[l - 1] = '\0';
}
}
if (blankline) {
/* done - str is now the string to check */
blankline = 0;
*strarg = str;
break;
/* else read the patterns */
} else if (add_pattern(patterns, patterns_count, (str[0] == ' ') ? &str[1] : str)) {
goto error;
}
if (str[0] == ' ') {
/* set invert-match */
(*patterns)[*patterns_count - 1].invert = 1;
}
}
if (!str || (blankline && (str[0] != '\0'))) {
/* corner case, no input after blankline meaning the pattern to check is empty */
if (create_empty_string(&str)) {
goto error;
}
}
*strarg = str;
return 0;
error:
free(str);
if (*infile) {
fclose(*infile);
*infile = NULL;
}
*strarg = NULL;
return 1;
}
static char *
modstr_init(void)
{
const char *module_start = "module yangre {"
"yang-version 1.1;"
"namespace urn:cesnet:libyang:yangre;"
"prefix re;"
"leaf pattern {"
" type string {";
return strdup(module_start);
}
static char *
modstr_add_pattern(char **modstr, const struct yr_pattern *pattern)
{
char *new;
const char *module_invertmatch = " { modifier invert-match; }";
const char *module_match = ";";
if (asprintf(&new, "%s pattern %s%s", *modstr, pattern->expr,
pattern->invert ? module_invertmatch : module_match) == -1) {
fprintf(stderr, "yangre error: memory allocation failed.\n");
return NULL;
}
free(*modstr);
*modstr = NULL;
return new;
}
static char *
modstr_add_ending(char **modstr)
{
char *new;
static const char *module_end = "}}}";
if (asprintf(&new, "%s%s", *modstr, module_end) == -1) {
fprintf(stderr, "yangre error: memory allocation failed.\n");
return NULL;
}
free(*modstr);
*modstr = NULL;
return new;
}
static int
create_module(struct yr_pattern *patterns, int patterns_count, char **mod)
{
int i;
char *new = NULL, *modstr;
if (!(modstr = modstr_init())) {
goto error;
}
for (i = 0; i < patterns_count; i++) {
if (!(new = modstr_add_pattern(&modstr, &patterns[i]))) {
goto error;
}
modstr = new;
}
if (!(new = modstr_add_ending(&modstr))) {
goto error;
}
*mod = new;
return 0;
error:
*mod = NULL;
free(new);
free(modstr);
return 1;
}
static void
print_verbose(struct yr_pattern *patterns, int patterns_count, char *str, LY_ERR match)
{
int i;
for (i = 0; i < patterns_count; i++) {
fprintf(stdout, "pattern %d: %s\n", i + 1, patterns[i].expr);
fprintf(stdout, "matching %d: %s\n", i + 1, patterns[i].invert ? "inverted" : "regular");
}
fprintf(stdout, "string : %s\n", str);
if (match == LY_SUCCESS) {
fprintf(stdout, "result : matching\n");
} else if (match == LY_EVALID) {
fprintf(stdout, "result : not matching\n");
} else {
fprintf(stdout, "result : error (%s)\n", ly_last_logmsg());
}
}
int
main(int argc, char *argv[])
{
LY_ERR match;
int i, opt_index = 0, ret = 1, verbose = 0;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{"file", required_argument, NULL, 'f'},
{"invert-match", no_argument, NULL, 'i'},
{"pattern", required_argument, NULL, 'p'},
{"version", no_argument, NULL, 'v'},
{"verbose", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
struct yr_pattern *patterns = NULL;
char *str = NULL, *modstr = NULL;
int patterns_count = 0;
struct ly_ctx *ctx = NULL;
struct lys_module *mod;
FILE *infile = NULL;
ly_bool info_printed = 0;
opterr = 0;
while ((i = getopt_long(argc, argv, "hf:ivVp:", options, &opt_index)) != -1) {
switch (i) {
case 'h':
help();
info_printed = 1;
break;
case 'f':
if (infile) {
help();
fprintf(stderr, "yangre error: multiple input files are not supported.\n");
goto cleanup;
} else if (patterns_count) {
help();
fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
goto cleanup;
}
if (parse_patterns_file(optarg, &infile, &patterns, &patterns_count, &str)) {
goto cleanup;
}
break;
case 'i':
if (!patterns_count || patterns[patterns_count - 1].invert) {
help();
fprintf(stderr, "yangre error: invert-match option must follow some pattern.\n");
goto cleanup;
}
patterns[patterns_count - 1].invert = 1;
break;
case 'p':
if (infile) {
help();
fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
goto cleanup;
}
if (add_pattern(&patterns, &patterns_count, optarg)) {
goto cleanup;
}
break;
case 'v':
version();
info_printed = 1;
break;
case 'V':
verbose = 1;
break;
default:
help();
if (optopt) {
fprintf(stderr, "yangre error: invalid option: -%c\n", optopt);
} else {
fprintf(stderr, "yangre error: invalid option: %s\n", argv[optind - 1]);
}
goto cleanup;
}
}
if (info_printed) {
ret = 0;
goto cleanup;
}
if (!str) {
/* check options compatibility */
if (optind >= argc) {
help();
fprintf(stderr, "yangre error: missing <string> parameter to process.\n");
goto cleanup;
} else if (!patterns_count) {
help();
fprintf(stderr, "yangre error: missing pattern parameter to use.\n");
goto cleanup;
}
str = argv[optind];
}
if (create_module(patterns, patterns_count, &modstr)) {
goto cleanup;
}
if (ly_ctx_new(NULL, 0, &ctx)) {
goto cleanup;
}
ly_set_log_clb(pattern_error);
if (lys_parse_mem(ctx, modstr, LYS_IN_YANG, &mod) || !mod->compiled || !mod->compiled->data) {
goto cleanup;
}
/* check the value */
match = lyd_value_validate(ctx, mod->compiled->data, str, strlen(str), NULL, NULL, NULL);
if (verbose) {
print_verbose(patterns, patterns_count, str, match);
}
if (match == LY_SUCCESS) {
ret = 0;
} else if (match == LY_EVALID) {
ret = 2;
} else {
ret = 1;
}
cleanup:
ly_ctx_destroy(ctx);
for (i = 0; i < patterns_count; i++) {
free(patterns[i].expr);
}
if (patterns_count) {
free(patterns);
}
free(modstr);
if (infile) {
fclose(infile);
free(str);
}
return ret;
}

118
tools/re/yangre.1 Normal file
View file

@ -0,0 +1,118 @@
.\" Manpage for yanglint.
.\" Process this file with
.\" groff -man -Tascii yangre.1
.\"
.TH YANGRE 1 "2018-11-09" "libyang"
.SH NAME
yangre \- YANG regular expression processor
.
.SH SYNOPSIS
.B yangre
[\-V] \-p \fIREGEXP\fP [\-i] [\-p \fIREGEXP\fP [\-i]...] \fISTRING\fP
.br
.B yangre
[\-V] \-f \fIFILE\fP
.
.SH DESCRIPTION
\fByangre\fP is a command-line tool to test and evaluate regular expressions
for use in YANG schemas. Supported regular expressions are defined by the
W3C's XML-Schema standard.
\fByangre\fP can be used either with regular expressions and a target string
on the command line or with input from a file. The latter is particularly
useful to avoid dealing with proper shell escaping of regular expression
patterns, which can be somewhat tricky.
.
.SH GENERAL OPTIONS
.TP
.BR "\-h\fR,\fP \-\^\-help"
.br
Outputs usage help and exits.
.TP
.BR "\-v\fR,\fP \-\^\-version"
.br
Outputs the version number and exits.
.TP
.BR "\-V\fR,\fP \-\^\-verbose"
Increases the verbosity level. If not specified, only errors are printed, with
each appearance it adds: warnings, verbose messages, debug messages (if compiled
with debug information).
.SH COMMAND LINE INPUT
.TP
.BR "\-p \fIREGEXP\fP\fR,\fP \-\^\-pattern=\fIREGEXP\fP"
.br
One or more regular expression patterns to be tested against the input
string. Supplied expressions are tested in the order they appear on the
command line. Testing is aborted when an expression does not match (or
does match, if the \fB-i\fP option is used.)
.TP
.BR "\-i\fR,\fP \-\^\-invert-match"
.br
Reverse match condition for the previous pattern. If the pattern matches,
an error is printed and evaluation is aborted.
.TP
.BR "\fISTRING\fP"
.br
Target text input to match the regular expression(s) against. The same
text is used for all regular expressions. Note that only the first
argument is used by \fByangre\fP, if it contains spaces or other shell
metacharacters they must be properly escaped. Additional arguments are
silently ignored.
.SH FILE INPUT
.TP
.BR "\-f \fIFILE\fP\fR,\fP \-\^\-file=\fIFILE\fP"
Read both patterns and target text from the specified input file.
\fIFILE\fP must consist of one or more YANG regular expressions, each on
their own line, followed by a blank line and one line of target text. No
preprocessing is done on file input, there are no comment lines and
whitespace is not stripped. A single space character at the beginning of
a pattern line inverts the match condition for the pattern on that line.
Patterns must still be properly quoted as mandated by the YANG standard.
.SH RETURN VALUES
.TP
0
.I Successful match
.br
The target text matched for all patterns.
.TP
1
.I Pattern mismatch
.br
One or more patterns did not match the target text. An error message is
printed to stderr describing which pattern was the first not to match.
.TP
255
.I Other error
.br
One or more patterns could not be processed or some other error occurred that
precluded processing.
.SH EXAMPLES
.IP \[bu] 2
Test a single pattern:
yangre -p 'te.*xt' text_text
.IP \[bu]
Test multiple patterns:
yangre -p '.*pat1' -p 'pat2.*' -p 'notpat' -i pat2testpat1
.IP \[bu]
Input from a file:
cat > /tmp/patterns <<EOF
.*pat1
pat2.*
notpat
pat2testpat1
EOF
yangre -f /tmp/patterns
.SH SEE ALSO
https://github.com/CESNET/libyang (libyang homepage and Git repository)
.
.SH AUTHORS
Radek Krejci <rkrejci@cesnet.cz>, Michal Vasko <mvasko@cesnet.cz>
.br
This man page was written by David Lamparter <equinox@diac24.net>
.
.SH COPYRIGHT
Copyright \(co 2015-2018 CESNET, a.l.e.