d_cfg_mgr.cc 11.1 KB
Newer Older
1
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15 16
#include <config.h>

17 18 19 20
#include <config/ccsession.h>
#include <d2/d2_log.h>
#include <dhcp/libdhcp++.h>
#include <d2/d_cfg_mgr.h>
21
#include <dhcpsrv/parsers/dhcp_parsers.h>
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#include <util/encode/hex.h>
#include <util/strutil.h>

#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>

#include <limits>
#include <iostream>
#include <vector>
#include <map>

using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::data;
using namespace isc::asiolink;

namespace isc {
namespace d2 {

// *********************** DCfgContextBase  *************************
44

45 46 47 48 49 50 51 52 53 54 55 56
DCfgContextBase::DCfgContextBase():
        boolean_values_(new BooleanStorage()),
        uint32_values_(new Uint32Storage()),
        string_values_(new StringStorage()) {
    }

DCfgContextBase::DCfgContextBase(const DCfgContextBase& rhs):
        boolean_values_(new BooleanStorage(*(rhs.boolean_values_))),
        uint32_values_(new Uint32Storage(*(rhs.uint32_values_))),
        string_values_(new StringStorage(*(rhs.string_values_))) {
}

57
const data::Element::Position&
58 59 60
DCfgContextBase::getParam(const std::string& name, bool& value, bool optional) {
    try {
        value = boolean_values_->getParam(name);
61
        return (boolean_values_->getPosition(name));
62 63 64 65 66 67 68
    } catch (DhcpConfigError& ex) {
        // If the parameter is not optional, re-throw the exception.
        if (!optional) {
            throw;
        }
    }

69 70
    return (data::Element::ZERO_POSITION());
}
71

72
const data::Element::Position&
73 74 75 76
DCfgContextBase::getParam(const std::string& name, uint32_t& value,
                          bool optional) {
    try {
        value = uint32_values_->getParam(name);
77
        return (uint32_values_->getPosition(name));
78 79 80 81 82 83
    } catch (DhcpConfigError& ex) {
        // If the parameter is not optional, re-throw the exception.
        if (!optional) {
            throw;
        }
    }
84 85

    return (data::Element::ZERO_POSITION());
86 87
}

88
const data::Element::Position&
89 90 91 92
DCfgContextBase::getParam(const std::string& name, std::string& value,
                          bool optional) {
    try {
        value = string_values_->getParam(name);
93
        return (string_values_->getPosition(name));
94 95 96 97 98 99
    } catch (DhcpConfigError& ex) {
        // If the parameter is not optional, re-throw the exception.
        if (!optional) {
            throw;
        }
    }
100 101

    return (data::Element::ZERO_POSITION());
102 103
}

104 105 106 107 108 109
DCfgContextBase::~DCfgContextBase() {
}

// *********************** DCfgMgrBase  *************************

DCfgMgrBase::DCfgMgrBase(DCfgContextBasePtr context)
110 111
    : parse_order_() {
    setContext(context);
112 113 114 115 116
}

DCfgMgrBase::~DCfgMgrBase() {
}

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
void
DCfgMgrBase::resetContext() {
    DCfgContextBasePtr context = createNewContext();
    setContext(context);
}

void
DCfgMgrBase::setContext(DCfgContextBasePtr& context) {
    if (!context) {
        isc_throw(DCfgMgrBaseError, "DCfgMgrBase: context cannot be NULL");
    }

    context_ = context;
}

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
isc::data::ConstElementPtr
DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
    LOG_DEBUG(dctl_logger, DBGLVL_COMMAND,
                DCTL_CONFIG_START).arg(config_set->str());

    if (!config_set) {
        return (isc::config::createAnswer(1,
                                    std::string("Can't parse NULL config")));
    }

    // The parsers implement data inheritance by directly accessing
    // configuration context. For this reason the data parsers must store
    // the parsed data into context immediately. This may cause data
    // inconsistency if the parsing operation fails after the context has been
    // modified. We need to preserve the original context here
    // so as we can rollback changes when an error occurs.
148 149
    DCfgContextBasePtr original_context = context_;
    resetContext();
150 151 152 153 154 155 156 157

    // Answer will hold the result returned to the caller.
    ConstElementPtr answer;

    // Holds the name of the element being parsed.
    std::string element_id;

    try {
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
        // Split the configuration into two maps. The first containing only
        // top-level scalar parameters (i.e. globals), the second containing
        // non-scalar or object elements (maps, lists, etc...).  This allows
        // us to parse and validate all of the global values before we do
        // objects which may depend on them.
        ElementMap params_map;
        ElementMap objects_map;

        isc::dhcp::ConfigPair config_pair;
        BOOST_FOREACH(config_pair, config_set->mapValue()) {
            std::string element_id = config_pair.first;
            isc::data::ConstElementPtr element = config_pair.second;
            switch (element->getType()) {
                case isc::data::Element::integer:
                case isc::data::Element::real:
                case isc::data::Element::boolean:
                case isc::data::Element::string:
                    params_map[element_id] = element;
                    break;
                default:
                    objects_map[element_id] = element;
                    break;
            }
        }

        // Parse the global, scalar parameters. These are "committed" to
        // the context to make them available during object parsing.
        boost::shared_ptr<MapElement> params_config(new MapElement());
        params_config->setValue(params_map);
        buildParams(params_config);

        // Now parse the configuration objects.
190 191 192 193

        // Use a pre-ordered list of element ids to parse the elements in a
        // specific order if the list (parser_order_) is not empty; otherwise
        // elements are parsed in the order the value_map presents them.
194
        if (!parse_order_.empty()) {
195 196
            // For each element_id in the parse order list, look for it in the
            // value map.  If the element exists in the map, pass it and it's
197 198 199 200 201
            // associated data in for parsing.
            // If there is no matching entry in the value map an error is
            // thrown.  Note, that elements tagged as "optional" from the user
            // perspective must still have default or empty entries in the
            // configuration set to be parsed.
202
            std::map<std::string, ConstElementPtr>::iterator it;
203
            BOOST_FOREACH(element_id, parse_order_) {
204 205
                it = objects_map.find(element_id);
                if (it != objects_map.end()) {
206
                    buildAndCommit(element_id, it->second);
207 208
                    // We parsed it, take it out of the list.
                    objects_map.erase(it);
209
                }
210
                else {
211 212
                    isc_throw(DCfgMgrBaseError,
                               "Element required by parsing order is missing: "
213 214
                               << element_id << " ("
                               << config_set->getPosition() << ")");
215 216 217 218 219
                }
            }

            // NOTE: When using ordered parsing, the parse order list MUST
            // include every possible element id that the value_map may contain.
220
            // Entries in the map that are not in the parse order, will not be
221 222 223
            // parsed. For now we will flag this as a programmatic error.  One
            // could attempt to adjust for this, by identifying such entries
            // and parsing them either first or last but which would be correct?
224 225
            // Better to hold the engineer accountable.  So, if there are any
            // left in the objects_map then they were not in the parse order.
226
            if (!objects_map.empty()) {
227 228 229 230 231
                std::ostringstream stream;
                bool add_comma = false;
                ConfigPair config_pair;
                BOOST_FOREACH(config_pair, objects_map) {
                    stream << ( add_comma ? ", " : "") << config_pair.first
232
                           << " (" << config_pair.second->getPosition() << ")";
233 234 235
                    add_comma = true;
                }

236
                isc_throw(DCfgMgrBaseError,
237 238
                        "Configuration contains elements not in parse order: "
                        << stream.str());
239 240 241 242 243
            }
        } else {
            // Order doesn't matter so iterate over the value map directly.
            // Pass each element and it's associated data in to be parsed.
            ConfigPair config_pair;
244
            BOOST_FOREACH(config_pair, objects_map) {
245 246 247 248 249 250
                element_id = config_pair.first;
                buildAndCommit(element_id, config_pair.second);
            }
        }

        // Everything was fine. Configuration set processed successfully.
251
        LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
252 253
        answer = isc::config::createAnswer(0, "Configuration committed.");

254 255
    } catch (const std::exception& ex) {
        LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(ex.what());
256
        answer = isc::config::createAnswer(1, ex.what());
257 258 259

        // An error occurred, so make sure that we restore original context.
        context_ = original_context;
260
        return (answer);
261 262 263 264 265
    }

    return (answer);
}

266 267 268 269 270
void
DCfgMgrBase::buildParams(isc::data::ConstElementPtr params_config) {
    // Loop through scalars parsing them and committing them to storage.
    BOOST_FOREACH(dhcp::ConfigPair param, params_config->mapValue()) {
        // Call derivation's method to create the proper parser.
271 272
        dhcp::ParserPtr parser(createConfigParser(param.first,
                                                  param.second->getPosition()));
273 274 275 276 277
        parser->build(param.second);
        parser->commit();
    }
}

278 279 280 281
void DCfgMgrBase::buildAndCommit(std::string& element_id,
                                 isc::data::ConstElementPtr value) {
    // Call derivation's implementation to create the appropriate parser
    // based on the element id.
282
    ParserPtr parser = createConfigParser(element_id, value->getPosition());
283
    if (!parser) {
284
        isc_throw(DCfgMgrBaseError, "Could not create parser");
285 286
    }

287 288 289 290 291 292 293 294 295 296
    // Invoke the parser's build method passing in the value. This will
    // "convert" the Element form of value into the actual data item(s)
    // and store them in parser's local storage.
    parser->build(value);

    // Invoke the parser's commit method. This "writes" the the data
    // item(s) stored locally by the parser into the context.  (Note that
    // parsers are free to do more than update the context, but that is an
    // nothing something we are concerned with here.)
    parser->commit();
297 298 299 300 301
}

}; // end of isc::dhcp namespace
}; // end of isc namespace