Adding upstream version 3.1.0+dfsg.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
64dbec996d
commit
cfcebb1a7d
569 changed files with 205393 additions and 0 deletions
25
tools/CMakeLists.txt
Normal file
25
tools/CMakeLists.txt
Normal 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
20
tools/config.h.in
Normal 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
48
tools/lint/CMakeLists.txt
Normal 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
112
tools/lint/cmd.c
Normal 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
394
tools/lint/cmd.h
Normal 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
210
tools/lint/cmd_add.c
Normal 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
172
tools/lint/cmd_clear.c
Normal 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
686
tools/lint/cmd_data.c
Normal 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
130
tools/lint/cmd_debug.c
Normal 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
115
tools/lint/cmd_extdata.c
Normal 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
131
tools/lint/cmd_feature.c
Normal 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
107
tools/lint/cmd_help.c
Normal 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
186
tools/lint/cmd_list.c
Normal 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
152
tools/lint/cmd_load.c
Normal 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
299
tools/lint/cmd_print.c
Normal 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;
|
||||
}
|
96
tools/lint/cmd_searchpath.c
Normal file
96
tools/lint/cmd_searchpath.c
Normal 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
114
tools/lint/cmd_verb.c
Normal 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
301
tools/lint/common.c
Normal 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
158
tools/lint/common.h
Normal 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
508
tools/lint/completion.c
Normal 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
25
tools/lint/completion.h
Normal 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
125
tools/lint/configuration.c
Normal 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);
|
||||
}
|
36
tools/lint/configuration.h
Normal file
36
tools/lint/configuration.h
Normal 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_ */
|
536
tools/lint/examples/README.md
Normal file
536
tools/lint/examples/README.md
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
8
tools/lint/examples/action-reply.xml
Normal file
8
tools/lint/examples/action-reply.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<cont1 xmlns="urn:module4">
|
||||
<list>
|
||||
<leaf1>key_val</leaf1>
|
||||
<act>
|
||||
<leaf3>some_output</leaf3>
|
||||
</act>
|
||||
</list>
|
||||
</cont1>
|
8
tools/lint/examples/action.xml
Normal file
8
tools/lint/examples/action.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<cont1 xmlns="urn:module4">
|
||||
<list>
|
||||
<leaf1>key_val</leaf1>
|
||||
<act>
|
||||
<leaf2>some_input</leaf2>
|
||||
</act>
|
||||
</list>
|
||||
</cont1>
|
24
tools/lint/examples/config-acm.xml
Normal file
24
tools/lint/examples/config-acm.xml
Normal 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>
|
24
tools/lint/examples/config-missing-key.xml
Normal file
24
tools/lint/examples/config-missing-key.xml
Normal 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>
|
27
tools/lint/examples/config-unknown-element.xml
Normal file
27
tools/lint/examples/config-unknown-element.xml
Normal 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>
|
27
tools/lint/examples/data-acm.xml
Normal file
27
tools/lint/examples/data-acm.xml
Normal 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>
|
12
tools/lint/examples/data-ip.xml
Normal file
12
tools/lint/examples/data-ip.xml
Normal 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>
|
27
tools/lint/examples/data-malformed-xml.xml
Normal file
27
tools/lint/examples/data-malformed-xml.xml
Normal 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>
|
26
tools/lint/examples/data-malformed-xml2.xml
Normal file
26
tools/lint/examples/data-malformed-xml2.xml
Normal 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>
|
26
tools/lint/examples/data-missing-key.xml
Normal file
26
tools/lint/examples/data-missing-key.xml
Normal 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>
|
27
tools/lint/examples/data-out-of-range-value.xml
Normal file
27
tools/lint/examples/data-out-of-range-value.xml
Normal 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>
|
29
tools/lint/examples/datastore.xml
Normal file
29
tools/lint/examples/datastore.xml
Normal 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>
|
1547
tools/lint/examples/iana-if-type.yang
Normal file
1547
tools/lint/examples/iana-if-type.yang
Normal file
File diff suppressed because it is too large
Load diff
725
tools/lint/examples/ietf-interfaces.yang
Normal file
725
tools/lint/examples/ietf-interfaces.yang
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
758
tools/lint/examples/ietf-ip.yang
Normal file
758
tools/lint/examples/ietf-ip.yang
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
412
tools/lint/examples/ietf-netconf-acm-when.yang
Normal file
412
tools/lint/examples/ietf-netconf-acm-when.yang
Normal 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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
447
tools/lint/examples/ietf-netconf-acm-when.yin
Normal file
447
tools/lint/examples/ietf-netconf-acm-when.yin
Normal 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: <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></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>
|
447
tools/lint/examples/ietf-netconf-acm-when2.yin
Normal file
447
tools/lint/examples/ietf-netconf-acm-when2.yin
Normal 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: <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></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>
|
411
tools/lint/examples/ietf-netconf-acm.yang
Normal file
411
tools/lint/examples/ietf-netconf-acm.yang
Normal 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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
tools/lint/examples/module1.yang
Normal file
5
tools/lint/examples/module1.yang
Normal file
|
@ -0,0 +1,5 @@
|
|||
module module1 {
|
||||
namespace "urn:yanglint:module";
|
||||
prefix m;
|
||||
leaf m { type string; }
|
||||
}
|
5
tools/lint/examples/module1b.yang
Normal file
5
tools/lint/examples/module1b.yang
Normal file
|
@ -0,0 +1,5 @@
|
|||
module module1b {
|
||||
namespace "urn:yanglint:module";
|
||||
prefix m;
|
||||
leaf mb { type string; }
|
||||
}
|
5
tools/lint/examples/module2.yang
Normal file
5
tools/lint/examples/module2.yang
Normal file
|
@ -0,0 +1,5 @@
|
|||
module module2 {
|
||||
namespace "urn:yanglint:module";
|
||||
prefix m;
|
||||
leaf m { ttype string; }
|
||||
}
|
10
tools/lint/examples/module2.yin
Normal file
10
tools/lint/examples/module2.yin
Normal 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>
|
8
tools/lint/examples/module3.yang
Normal file
8
tools/lint/examples/module3.yang
Normal 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; }
|
||||
}
|
||||
}
|
52
tools/lint/examples/module4.yang
Normal file
52
tools/lint/examples/module4.yang
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
8
tools/lint/examples/nested-notification.xml
Normal file
8
tools/lint/examples/nested-notification.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<cont1 xmlns="urn:module4">
|
||||
<list>
|
||||
<leaf1>key_val</leaf1>
|
||||
<notif1>
|
||||
<leaf4>some_value</leaf4>
|
||||
</notif1>
|
||||
</list>
|
||||
</cont1>
|
3
tools/lint/examples/notification.xml
Normal file
3
tools/lint/examples/notification.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<notif2 xmlns="urn:module4">
|
||||
<leaf7/>
|
||||
</notif2>
|
5
tools/lint/examples/rpc-reply.xml
Normal file
5
tools/lint/examples/rpc-reply.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<rpc xmlns="urn:module4">
|
||||
<cont2>
|
||||
<leaf6/>
|
||||
</cont2>
|
||||
</rpc>
|
3
tools/lint/examples/rpc.xml
Normal file
3
tools/lint/examples/rpc.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<rpc xmlns="urn:module4">
|
||||
<leaf5>some_input</leaf5>
|
||||
</rpc>
|
64
tools/lint/examples/sm-context-extension.xml
Normal file
64
tools/lint/examples/sm-context-extension.xml
Normal 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>
|
54
tools/lint/examples/sm-context-main.xml
Normal file
54
tools/lint/examples/sm-context-main.xml
Normal 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>
|
19
tools/lint/examples/sm-data.xml
Normal file
19
tools/lint/examples/sm-data.xml
Normal 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>
|
39
tools/lint/examples/sm-extension.yang
Normal file
39
tools/lint/examples/sm-extension.yang
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
tools/lint/examples/sm-main.yang
Normal file
32
tools/lint/examples/sm-main.yang
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
21
tools/lint/examples/sm-mod.yang
Normal file
21
tools/lint/examples/sm-mod.yang
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
25
tools/lint/linenoise/LICENSE
Normal file
25
tools/lint/linenoise/LICENSE
Normal 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.
|
1218
tools/lint/linenoise/linenoise.c
Normal file
1218
tools/lint/linenoise/linenoise.c
Normal file
File diff suppressed because it is too large
Load diff
94
tools/lint/linenoise/linenoise.h
Normal file
94
tools/lint/linenoise/linenoise.h
Normal 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
134
tools/lint/main.c
Normal 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 = ∅
|
||||
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
790
tools/lint/main_ni.c
Normal 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
22
tools/lint/main_ni_only.c
Normal 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
136
tools/lint/yanglint.1
Normal 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
344
tools/lint/yl_opt.c
Normal 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
237
tools/lint/yl_opt.h
Normal 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_ */
|
212
tools/lint/yl_schema_features.c
Normal file
212
tools/lint/yl_schema_features.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
84
tools/lint/yl_schema_features.h
Normal file
84
tools/lint/yl_schema_features.h
Normal 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
20
tools/re/CMakeLists.txt
Normal 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
461
tools/re/main.c
Normal 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
118
tools/re/yangre.1
Normal 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.
|
Loading…
Add table
Add a link
Reference in a new issue