Commit c0970023 authored by Francis Dupont's avatar Francis Dupont

[5-netconf-config] Added netconf syntax

parent 17272324
...@@ -57,6 +57,8 @@ nobase_dist_doc_DATA += examples/kea6/simple.json ...@@ -57,6 +57,8 @@ nobase_dist_doc_DATA += examples/kea6/simple.json
nobase_dist_doc_DATA += examples/kea6/softwire46.json nobase_dist_doc_DATA += examples/kea6/softwire46.json
nobase_dist_doc_DATA += examples/kea6/stateless.json nobase_dist_doc_DATA += examples/kea6/stateless.json
nobase_dist_doc_DATA += examples/kea6/with-ddns.json nobase_dist_doc_DATA += examples/kea6/with-ddns.json
nobase_dist_doc_DATA += examples/netconf/comments.json
nobase_dist_doc_DATA += examples/netconf/simple.json
devel: devel:
mkdir -p html mkdir -p html
......
// This is a example of a configuration for Netconf.
// It uses embedded (i.e., which will be included in configuration objects
// and not stripped by at lexical analysis) comments.
{
"Netconf":
{
// Global scope
"comment": "The Netconf Agent",
// In servers
"managed-servers":
{
"dhcp4":
{
"comment": "the model is mandatory",
"model": "kea-dhcp4-server",
// In control socket.
"control-socket":
{
"comment": "using unix/local socket",
"type": "unix",
"name": "/path/to/the/unix/socket-v4"
}
}
}
},
"Logging":
{
// In loggers
"loggers": [
{
"comment": "A logger",
"name": "kea-ctrl-agent"
}
]
}
}
// This is a simple example of a configuration for Netconf.
// This server provides YANG interface for all Kea servers and agent.
{
"Netconf":
{
// This map specifies how each server is managed:
// the YANG model to use and the control channel.
// Currently three control channel types are supported:
// "stdout" which output the configuration on the standard output,
// "unix" which uses the local control channel supported by
// "dhcp4" and "dhcp6" servers ("d2" support is not yet merged),
// and "http" which uses the Control agent "ca" to manage itself or
// to forward commands to "dhcp4" or "dhcp6" (same comment about "d2".
"managed-servers":
{
// This is how Netconf can communicate with the DHCPv4 server.
"dhcp4":
{
"comment": "DHCP4 server",
"model": "kea-dhcp4-server",
"control-socket":
{
"type": "unix",
"name": "/path/to/the/unix/socket-v4"
}
},
// DHCPv6 parameters.
"dhcp6":
{
"model": "kea-dhcp6-server",
"control-socket":
{
"type": "unix",
"name": "/path/to/the/unix/socket-v6"
}
},
// Currently the DHCP-DDNS (nicknamed D2) server does not support
// command channel yet.
"d2":
{
"model": "kea-dhcp-ddns",
"control-socket":
{
"type": "stdout",
"user-context": { "in-use": false }
}
},
// Of course the Control Agent (nicknamed CA) supports HTTP.
"ca":
{
"model": "kea-ctrl-agent",
"control-socket":
{
"type": "http",
"host": "127.0.0.1",
"port": 8000
}
}
},
// Netconf is able to load hook libraries that augment its operation.
// The primary functionality is the ability to add new commands.
"hooks-libraries": [
// Hook libraries list may contain more than one library.
{
// The only necessary parameter is the library filename.
"library": "/opt/local/netconf-commands.so",
// Some libraries may support parameters. Make sure you
// type this section carefully, as the CA does not validate
// it (because the format is library specific).
"parameters": {
"param1": "foo"
}
}
]
},
// Similar to other Kea components, Netconf also uses logging.
"Logging":
{
"loggers": [
{
"name": "kea-netconf",
"output_options": [
{
"output": "/var/log/kea-netconf.log",
// Several additional parameters are possible in addition
// to the typical output. Flush determines whether logger
// flushes output to a file. Maxsize determines maximum
// filesize before the file is being rotated. maxver
// specifies the maximum number of rotated files being
// kept.
"flush": true,
"maxsize": 204800,
"maxver": 4
}
],
"severity": "INFO",
"debuglevel": 0
}
]
}
}
SUBDIRS = . tests SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
...@@ -17,7 +16,7 @@ CLEANFILES = *.gcno *.gcda netconf_messages.h netconf_messages.cc s-messages ...@@ -17,7 +16,7 @@ CLEANFILES = *.gcno *.gcda netconf_messages.h netconf_messages.cc s-messages
man_MANS = kea-netconf.8 man_MANS = kea-netconf.8
DISTCLEANFILES = $(man_MANS) DISTCLEANFILES = $(man_MANS)
EXTRA_DIST = $(man_MANS) kea-netconf.xml EXTRA_DIST = $(man_MANS) kea-netconf.xml
#EXTRA_DIST += netconf.dox netconf_hooks.dox netconfo6.dox #EXTRA_DIST += netconf.dox netconf_hooks.dox
#EXTRA_DIST += netconf_parser.yy #EXTRA_DIST += netconf_parser.yy
if GENERATE_DOCS if GENERATE_DOCS
...@@ -47,18 +46,38 @@ BUILT_SOURCES = netconf_messages.h netconf_messages.cc ...@@ -47,18 +46,38 @@ BUILT_SOURCES = netconf_messages.h netconf_messages.cc
noinst_LTLIBRARIES = libnetconf.la noinst_LTLIBRARIES = libnetconf.la
libnetconf_la_SOURCES = netconf_log.cc netconf_log.h libnetconf_la_SOURCES = netconf_log.cc netconf_log.h
libnetconf_la_SOURCES += parser_context.cc parser_context.h
libnetconf_la_SOURCES += parser_context_decl.h netconf_lexer.ll
libnetconf_la_SOURCES += netconf_parser.cc netconf_parser.h
libnetconf_la_SOURCES += location.hh position.hh stack.hh
nodist_libnetconf_la_SOURCES = netconf_messages.h netconf_messages.cc nodist_libnetconf_la_SOURCES = netconf_messages.h netconf_messages.cc
EXTRA_DIST += netconf_messages.mes EXTRA_DIST += netconf_messages.mes
EXTRA_DIST += netconf_lexer.ll
EXTRA_DIST += netconf_parser.yy
sbin_PROGRAMS = kea-netconf sbin_PROGRAMS = kea-netconf
kea_netconf_SOURCES = main.cc kea_netconf_SOURCES = main.cc
kea_netconf_LDADD = libnetconf.la kea_netconf_LDADD = libnetconf.la
kea_netconf_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
kea_netconf_LDADD += $(top_builddir)/src/lib/process/libkea-process.la kea_netconf_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
kea_netconf_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
kea_netconf_LDADD += $(top_builddir)/src/lib/yang/libkea-yang.la
kea_netconf_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
kea_netconf_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
kea_netconf_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
kea_netconf_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
kea_netconf_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
kea_netconf_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
kea_netconf_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
kea_netconf_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
kea_netconf_LDADD += $(top_builddir)/src/lib/log/libkea-log.la kea_netconf_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
kea_netconf_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
kea_netconf_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
kea_netconf_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
kea_netconf_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la kea_netconf_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
kea_netconf_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS) kea_netconf_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS)
...@@ -67,7 +86,7 @@ kea_netconfdir = $(pkgdatadir) ...@@ -67,7 +86,7 @@ kea_netconfdir = $(pkgdatadir)
if GENERATE_PARSER if GENERATE_PARSER
#parser: netconf_lexer.cc location.hh position.hh stack.hh netconf_parser.cc netconf_parser.h parser: netconf_lexer.cc location.hh position.hh stack.hh netconf_parser.cc netconf_parser.h
# @echo "Flex/bison files regenerated" # @echo "Flex/bison files regenerated"
# --- Flex/Bison stuff below -------------------------------------------------- # --- Flex/Bison stuff below --------------------------------------------------
...@@ -79,11 +98,11 @@ if GENERATE_PARSER ...@@ -79,11 +98,11 @@ if GENERATE_PARSER
# Call flex with -s to check that the default rule can be suppressed # Call flex with -s to check that the default rule can be suppressed
# Call bison with -W to get warnings like unmarked empty rules # Call bison with -W to get warnings like unmarked empty rules
# Note C++11 deprecated register still used by flex < 2.6.0 # Note C++11 deprecated register still used by flex < 2.6.0
#location.hh position.hh stack.hh netconf_parser.cc netconf_parser.h: netconf_parser.yy location.hh position.hh stack.hh netconf_parser.cc netconf_parser.h: netconf_parser.yy
# $(YACC) --defines=netconf_parser.h --report=all --report-file=netconf_parser.report -o netconf_parser.cc netconf_parser.yy $(YACC) --defines=netconf_parser.h --report=all --report-file=netconf_parser.report -o netconf_parser.cc netconf_parser.yy
#netconf_lexer.cc: netconf_lexer.ll netconf_lexer.cc: netconf_lexer.ll
# $(LEX) --prefix netconf_ -o netconf_lexer.cc netconf_lexer.ll $(LEX) --prefix netconf_ -o netconf_lexer.cc netconf_lexer.ll
else else
......
This diff is collapsed.
This diff is collapsed.
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <netconf/parser_context.h>
#include <netconf/netconf_parser.h>
#include <exceptions/exceptions.h>
//#include <cc/dhcp_config_error.h>
#include <cc/data.h>
#include <fstream>
#include <limits>
namespace isc {
namespace netconf {
ParserContext::ParserContext()
: sfile_(0), ctx_(NO_KEYWORDS), trace_scanning_(false), trace_parsing_(false)
{
}
ParserContext::~ParserContext()
{
}
isc::data::ElementPtr
ParserContext::parseString(const std::string& str, ParserType parser_type)
{
scanStringBegin(str, parser_type);
return (parseCommon());
}
isc::data::ElementPtr
ParserContext::parseFile(const std::string& filename, ParserType parser_type) {
FILE* f = fopen(filename.c_str(), "r");
if (!f) {
isc_throw(ParseError, "Unable to open file " << filename);
}
scanFileBegin(f, filename, parser_type);
return (parseCommon());
}
isc::data::ElementPtr
ParserContext::parseCommon() {
isc::netconf::NetconfParser parser(*this);
// Uncomment this to get detailed parser logs.
// trace_parsing_ = true;
parser.set_debug_level(trace_parsing_);
try {
int res = parser.parse();
if (res != 0) {
isc_throw(ParseError, "Parser abort");
}
scanEnd();
}
catch (...) {
scanEnd();
throw;
}
if (stack_.size() == 1) {
return (stack_[0]);
} else {
isc_throw(ParseError, "Expected exactly one terminal Element expected, found "
<< stack_.size());
}
}
void
ParserContext::error(const isc::netconf::location& loc, const std::string& what)
{
isc_throw(ParseError, loc << ": " << what);
}
void
ParserContext::error(const std::string& what)
{
isc_throw(ParseError, what);
}
void
ParserContext::fatal(const std::string& what)
{
isc_throw(ParseError, what);
}
isc::data::Element::Position
ParserContext::loc2pos(isc::netconf::location& loc)
{
const std::string& file = *loc.begin.filename;
const uint32_t line = loc.begin.line;
const uint32_t pos = loc.begin.column;
return (isc::data::Element::Position(file, line, pos));
}
void
ParserContext::require(const std::string& name,
isc::data::Element::Position open_loc,
isc::data::Element::Position close_loc)
{
ConstElementPtr value = stack_.back()->get(name);
if (!value) {
isc_throw(ParseError,
"missing parameter '" << name << "' ("
<< stack_.back()->getPosition() << ") ["
<< contextName() << " map between "
<< open_loc << " and " << close_loc << "]");
}
}
void
ParserContext::enter(const LexerContext& ctx)
{
cstack_.push_back(ctx_);
ctx_ = ctx;
}
void
ParserContext::leave()
{
if (cstack_.empty()) {
fatal("unbalanced syntactic context");
}
ctx_ = cstack_.back();
cstack_.pop_back();
}
const std::string
ParserContext::contextName()
{
switch (ctx_) {
case NO_KEYWORDS:
return ("__no keywords__");
case CONFIG:
return ("toplevel");
case NETCONF:
return ("Netconf");
case LOGGING:
return ("Logging");
case MANAGED_SERVERS:
return ("managed-servers");
case SERVER:
return ("[managed-]server");
case CONTROL_SOCKET:
return ("control-socket");
case SOCKET_TYPE:
return ("[socket-]type");
case HOOKS_LIBRARIES:
return ("hooks-libraries");
case LOGGERS:
return ("loggers");
case OUTPUT_OPTIONS:
return ("output-options");
default:
return ("__unknown__");
}
}
};
};
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef NETCONF_PARSER_CONTEXT_H
#define NETCONF_PARSER_CONTEXT_H
#include <string>
#include <map>
#include <vector>
#include <netconf/netconf_parser.h>
#include <netconf/parser_context_decl.h>
#include <cc/dhcp_config_error.h>
#include <exceptions/exceptions.h>
// Tell Flex the lexer's prototype ...
#define YY_DECL isc::netconf::NetconfParser::symbol_type netconf_lex (ParserContext& driver)
// ... and declare it for the parser's sake.
YY_DECL;
namespace isc {
namespace netconf {
/// @brief Parser context is a wrapper around flex/bison instances dedicated to
/// Netconf-agent config file parser.
///
/// It follows the same principle as other components. The primary interface
/// are @ref parseString and @ref parseFile methods. All other methods are
/// public for testing purposes only. This interface allows parsing the
/// whole configuration with syntactic checking (which is by far the most
/// frequent use), but it also allows parsing input as generic JSON or
/// parse only content of the Netconf-agent object, which is a subset
/// of full grammar (this will be very useful for unit-tests to not duplicate
/// unnecessary parts of the config file).
class ParserContext
{
public:
/// @brief Defines currently supported scopes
///
/// NetconfParser is able to parse several types of scope. Usually,
/// when it parses a config file, it expects the data to have a map
/// with Netconf-agent in it and all the parameters within that map.
/// However, sometimes the parser is expected to parse only a subset
/// of that information.
typedef enum {
/// This parser will parse the content as generic JSON.
PARSER_JSON,
/// This parser will expect the content as Netconf config wrapped
/// in a map (that's the regular config file)
PARSER_NETCONF,
/// This parser will expect only the content of Netconf.
PARSER_SUB_NETCONF
} ParserType;
/// @brief Default constructor.
ParserContext();
/// @brief destructor
virtual ~ParserContext();
/// @brief JSON elements being parsed.
std::vector<isc::data::ElementPtr> stack_;
/// @brief Method called before scanning starts on a string.
///
/// @param str string to be parsed
/// @param type specifies expected content
void scanStringBegin(const std::string& str, ParserType type);
/// @brief Method called before scanning starts on a file.
///
/// @param f stdio FILE pointer
/// @param filename file to be parsed
/// @param type specifies expected content
void scanFileBegin(FILE* f, const std::string& filename, ParserType type);
/// @brief Method called after the last tokens are scanned.
void scanEnd();
/// @brief Divert input to an include file.
///
/// @param filename file to be included
void includeFile(const std::string& filename);
/// @brief Run the parser on the string specified.
///
/// This method parses specified string. Depending on the value of
/// parser_type, parser may either check only that the input is valid
/// JSON, or may do more specific syntax checking. See @ref ParserType
/// for supported syntax checkers.
///
/// @param str string to be parsed
/// @param parser_type specifies expected content (usually NETCONF or generic JSON)
/// @return Element structure representing parsed text.
isc::data::ElementPtr parseString(const std::string& str,
ParserType parser_type);
/// @brief Run the parser on the file specified.
///
/// This method parses specified file. Depending on the value of
/// parser_type, parser may either check only that the input is valid
/// JSON, or may do more specific syntax checking. See @ref ParserType
/// for supported syntax checkers.
///
/// @param filename file to be parsed
/// @param parser_type specifies expected content (usually PARSER_NETCONF
/// or PARSER_JSON)
/// @return Element structure representing parsed text.
isc::data::ElementPtr parseFile(const std::string& filename,
ParserType parser_type);
/// @brief Error handler
///
/// @param loc location within the parsed file when experienced a problem.
/// @param what string explaining the nature of the error.
/// @throw ParseError
void error(const isc::netconf::location& loc, const std::string& what);
/// @brief Error handler
///
/// This is a simplified error reporting tool for possible future
/// cases when the NetconfParser is not able to handle the packet.
///
/// @param what string explaining the nature of the error.
/// @throw ParseError
void error(const std::string& what);
/// @brief Fatal error handler
///
/// This is for should not happen but fatal errors.
/// Used by YY_FATAL_ERROR macro so required to be static.
///
/// @param what string explaining the nature of the error.
/// @throw ParseError
static void fatal(const std::string& what);
/// @brief Converts bison's position to one understandable by isc::data::Element
///
/// Convert a bison location into an element position
/// (take the begin, the end is lost)
///
/// @param loc location in bison format
/// @return Position in format accepted by Element
isc::data::Element::Position loc2pos(isc::netconf::location& loc);
/// @brief Check if a required parameter is present
///
/// Check if a required parameter is present in the map at the top
/// of the stack and raise an error when it is not.
///
/// @param name name of the parameter to check
/// @param open_loc location of the opening curly bracket
/// @param close_loc ocation of the closing curly bracket
/// @throw ParseError
void require(const std::string& name,
isc::data::Element::Position open_loc,
isc::data::Element::Position close_loc);
/// @brief Defines syntactic contexts for lexical tie-ins
typedef enum {
///< This one is used in pure JSON mode.
NO_KEYWORDS,
///< Used while parsing top level (that contains Netconf, Logging and others)
CONFIG,
///< Used while parsing content of Netconf.
NETCONF,
///< Used while parsing content of Logging.
LOGGING,
/// Used while parsing Netconf/managed-servers.
MANAGED_SERVERS,
///< Used while parsing Netconf/managed-servers/*.
SERVER,
///< Used while parsing Netconf/manages-servers/*/control-socket
CONTROL_SOCKET,
///< Used while parsing Netconf/managed-servers/*/control-socket/type.
SOCKET_TYPE,
///< Used while parsing Netconf/hooks-libraries.
HOOKS_LIBRARIES,
///< Used while parsing Logging/loggers structures.
LOGGERS,
///< Used while parsing Logging/loggers/output_options structures.
OUTPUT_OPTIONS
} LexerContext;
/// @brief File name
std::string file_;
/// @brief File name stack
std::vector<std::string> files_;
/// @brief Location of the current token
///
/// The lexer will keep updating it. This variable will be useful
/// for logging errors.