Kea 2.5.8
libkea-cc - Kea Configuration Utilities Library

Simple JSON Parser

Since the early beginnings, our configuration parsing code was a mess. It started back in 2011 when Tomek joined ISC recently and was told to implement Kea configuration handling in similar way as DNS Auth module. The code grew over time (DHCP configuration is significantly more complex than DNS, with more interdependent values) and as of Kea 1.1 release it became very difficult to manage. The decision has been made to significantly refactor or even partially rewrite the parser code. The design for this effort is documented here: https://gitlab.isc.org/isc-projects/kea/wikis/designs/simple-parser-design. It discusses the original issues and the proposed architecture.

There are several aspects of this new approach. The base class for all parsers is isc::data::SimpleParser. It simplifies the parsers based on DhcpConfigParser by rejecting the concept of build/commit phases. Instead, there should be a single method called parse that takes ConstElementPtr as a single parameter (that's the JSON structures to be parsed) and returns the config structure to be used in CfgMgr. An example of such a method can be the following:

std::pair<OptionDescriptor, std::string>
OptionDataParser::parse(isc::data::ConstElementPtr single_option)
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:29

Since each derived class will have the same parameter, but a different return type, it's not possible to use virtual methods mechanism. That's perfectly ok, though, as there is only a single instance of the class needed to parse arbitrary number of parameters of the same type. There is no need to keep pointers to the parser object. As such there are fewer incentives to have one generic way to handle all parsers.

Default values in Simple Parser

Another simplification comes from the fact that almost all parameters are mandatory in SimpleParser. One source of complexities in the old parser was the necessity to deal with optional parameters. Simple parser deals with that by explicitly requiring the input structure to have all parameters filled. Obviously, it's not feasible to expect everyone to always specify all parameters, therefore there's an easy way to fill missing parameters with their default values. There are several methods to do this, but the most generic one is:

static size_t
const SimpleDefaults& default_values);
static size_t setDefaults(isc::data::ElementPtr scope, const SimpleDefaults &default_values)
Sets the default values.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:28

It takes a pointer to element to be filled with default values and vector of default values. Having those values specified in a single place in a way that can easily be read even by non-programmers is a big advantage of this approach. Here's an example from simple_parser.cc file:

const SimpleDefaults OPTION6_DEF_DEFAULTS = {
{ "record-types", Element::string, ""},
{ "space", Element::string, "dhcp6"},
{ "array", Element::boolean, "false"},
{ "encapsulate", Element::string, "" }
};

This array (which technically is implemented as a vector and initialized the C++11 way) can be passed to the aforementioned setDefaults. That code will iterate over all default values and see if there are explicit values provided. If not, the gaps will be filled with default values. There are also convenience methods specified for filling in option data defaults, option definition defaults and setAllDefaults that sets all defaults (starts with global, but then walks down the Element tree and fills defaults in subsequent scopes).

Inheriting parameters between scopes

SimpleParser provides a mechanism to inherit parameters between scopes, e.g. to inherit global parameters in the subnet scope if more specific values are not defined in the subnet scope. This is achieved by calling

static size_t SimpleParser::deriveParams(isc::data::ConstElementPtr parent,
const ParamsList& params);

ParamsList is a simple vector<string>. There will be more specific methods implemented in the future, but for the time being only isc::data::SimpleParser::deriveParams is implemented.

Multi-Threading Consideration for Configuration Utilities

No configuration utility is thread safe. For instance stamped values are not thread safe so any read access must be done in a context where write access at the same time is not possible. Note that configuration is performed by the main thread with service threads stopped so this constraint is fulfilled.