Commit f39d2080 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3477'

parents adc83754 196f0896
......@@ -196,6 +196,11 @@ D2CfgMgr::getD2Params() {
return (getD2CfgContext()->getD2Params());
}
std::string
D2CfgMgr::getConfigSummary(const uint32_t) {
return (getD2Params()->getConfigSummary());
}
void
D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
// Base class build creates parses and invokes build on each parser.
......
......@@ -238,6 +238,14 @@ public:
/// @return reference to const D2ParamsPtr
const D2ParamsPtr& getD2Params();
/// @brief Returns configuration summary in the textual format.
///
/// @param selection Bitfield which describes the parts of the configuration
/// to be returned. This parameter is ignored for the D2.
///
/// @return Summary of the configuration in the textual format.
virtual std::string getConfigSummary(const uint32_t selection);
protected:
/// @brief Performs the parsing of the given "params" element.
///
......
......@@ -22,6 +22,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <sstream>
#include <string>
namespace isc {
......@@ -88,6 +89,14 @@ D2Params::validateContents() {
}
}
std::string
D2Params::getConfigSummary() const {
std::ostringstream s;
s << "listening on " << getIpAddress() << ", port " << getPort()
<< ", using " << ncrProtocolToString(ncr_protocol_);
return (s.str());
}
bool
D2Params::operator == (const D2Params& other) const {
return ((ip_address_ == other.ip_address_) &&
......
......@@ -211,6 +211,15 @@ public:
return(ncr_format_);
}
/// @brief Return summary of the configuration used by D2.
///
/// The returned summary of the configuration is meant to be appended to
/// the log message informing about the successful completion of the
/// D2 configuration.
///
/// @return Configuration summary in the textual format.
std::string getConfigSummary() const;
/// @brief Compares two D2Paramss for equality
bool operator == (const D2Params& other) const;
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -246,7 +246,7 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
}
// Everything was fine. Configuration set processed successfully.
LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg("");
LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
answer = isc::config::createAnswer(0, "Configuration committed.");
} catch (const std::exception& ex) {
......
......@@ -308,6 +308,18 @@ public:
return (context_);
}
/// @brief Returns configuration summary in the textual format.
///
/// This method returns the brief text describing the current configuration.
/// It may be used for logging purposes, e.g. whn the new configuration is
/// committed to notify a user about the changes in configuration.
///
/// @param selection Bitfield which describes the parts of the configuration
/// to be returned.
///
/// @return Summary of the configuration in the textual format.
virtual std::string getConfigSummary(const uint32_t selection) = 0;
protected:
/// @brief Parses a set of scalar configuration elements into global
/// parameters
......
......@@ -376,6 +376,10 @@ TEST_F(D2CfgMgrTest, validParamsEntry) {
// Verify that the global scalars have the proper values.
EXPECT_EQ(isc::asiolink::IOAddress("3001::5"),
d2_params_->getIpAddress());
// Verify the configuration summary.
EXPECT_EQ("listening on 3001::5, port 777",
d2_params_->getConfigSummary());
}
/// @brief Tests default values for D2Params.
......
......@@ -57,6 +57,11 @@ public:
const isc::data::Element::Position& /* pos */) {
return (isc::dhcp::ParserPtr());
}
/// @brief Returns summary of configuration in the textual format.
virtual std::string getConfigSummary(const uint32_t) {
return ("");
}
};
/// @brief Test fixture class for testing DCfgMgrBase class.
......
......@@ -180,6 +180,13 @@ public:
virtual isc::data::ConstElementPtr command(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief Returns configuration summary in the textual format.
///
/// @return Always an empty string.
virtual std::string getConfigSummary(const uint32_t) {
return ("");
}
// @brief Destructor
virtual ~DStubProcess();
};
......@@ -701,6 +708,13 @@ public:
const isc::data::Element::Position& pos
= isc::data::Element::Position());
/// @brief Returns a summary of the configuration in the textual format.
///
/// @return Always an empty string.
virtual std::string getConfigSummary(const uint32_t) {
return ("");
}
/// @brief A list for remembering the element ids in the order they were
/// parsed.
ElementIdList parsed_order_;
......
......@@ -496,9 +496,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
return (answer);
}
/// @todo: Append most essential info here (like "2 new subnets configured")
string config_details;
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND,
DHCP4_CONFIG_START).arg(config_set->str());
......@@ -657,7 +654,9 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
return (answer);
}
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE).arg(config_details);
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE)
.arg(CfgMgr::instance().getConfiguration()->
getConfigSummary(Configuration::CFGSEL_ALL4));
// Everything was fine. Configuration is successful.
answer = isc::config::createAnswer(0, "Configuration committed.");
......
......@@ -698,9 +698,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
return (answer);
}
/// @todo: Append most essential info here (like "2 new subnets configured")
string config_details;
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND,
DHCP6_CONFIG_START).arg(config_set->str());
......@@ -858,7 +855,9 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
return (answer);
}
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE)
.arg(CfgMgr::instance().getConfiguration()->
getConfigSummary(Configuration::CFGSEL_ALL6));
// Everything was fine. Configuration is successful.
answer = isc::config::createAnswer(0, "Configuration committed.");
......
......@@ -60,7 +60,7 @@ libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
libkea_dhcpsrv_la_SOURCES += logging.cc logging.h
libkea_dhcpsrv_la_SOURCES += configuration.h
libkea_dhcpsrv_la_SOURCES += configuration.h configuration.cc
libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
if HAVE_MYSQL
......
......@@ -250,12 +250,12 @@ public:
/// completely new?
void deleteSubnets6();
/// @brief returns const reference to all subnets6
/// @brief Returns pointer to the collection of all IPv4 subnets.
///
/// This is used in a hook (subnet4_select), where the hook is able
/// to choose a different subnet. Server code has to offer a list
/// of possible choices (i.e. all subnets).
/// @return a pointer to const Subnet6 collection
/// @return a pointer to const Subnet4 collection
const Subnet4Collection* getSubnets4() const {
return (&subnets4_);
}
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// 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.
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/configuration.h>
#include <sstream>
namespace isc {
namespace dhcp {
std::string
Configuration::getConfigSummary(const uint32_t selection) const {
std::ostringstream s;
size_t subnets_num;
if ((selection & CFGSEL_SUBNET4) == CFGSEL_SUBNET4) {
subnets_num = CfgMgr::instance().getSubnets4()->size();
if (subnets_num > 0) {
s << "added IPv4 subnets: " << subnets_num;
} else {
s << "no IPv4 subnets!";
}
s << "; ";
}
if ((selection & CFGSEL_SUBNET6) == CFGSEL_SUBNET6) {
subnets_num = CfgMgr::instance().getSubnets6()->size();
if (subnets_num > 0) {
s << "added IPv6 subnets: " << subnets_num;
} else {
s << "no IPv6 subnets!";
}
s << "; ";
}
if ((selection & CFGSEL_DDNS) == CFGSEL_DDNS) {
bool ddns_enabled = CfgMgr::instance().ddnsEnabled();
s << "DDNS: " << (ddns_enabled ? "enabled" : "disabled") << "; ";
}
if (s.tellp() == 0) {
s << "no config details available";
}
std::string summary = s.str();
size_t last_separator_pos = summary.find_last_of(";");
if (last_separator_pos == summary.length() - 2) {
summary.erase(last_separator_pos);
}
return (summary);
}
}
}
......@@ -23,6 +23,8 @@
namespace isc {
namespace dhcp {
class CfgMgr;
/// @brief Defines single logging destination
///
/// This structure is used to keep log4cplus configuration parameters.
......@@ -33,7 +35,7 @@ struct LoggingDestination {
/// Values accepted are: stdout, stderr, syslog, syslog:name.
/// Any other destination will be considered a file name.
std::string output_;
/// @brief Maximum number of log files in rotation
int maxver_;
......@@ -53,10 +55,10 @@ struct LoggingDestination {
/// "maxver": 8,
/// "maxsize": 204800
/// }
/// ],
/// ],
/// "severity": "WARN",
/// "debuglevel": 99
/// },
/// },
struct LoggingInfo {
/// @brief logging name
......@@ -82,8 +84,53 @@ typedef std::vector<isc::dhcp::LoggingInfo> LoggingInfoStorage;
/// @todo Migrate all other configuration parameters from cfgmgr.h here
struct Configuration {
/// @name Constants for selection of parameters returned by @c getConfigSummary
///
//@{
/// Nothing selected
static const uint32_t CFGSEL_NONE = 0x00000000;
/// Number of IPv4 subnets
static const uint32_t CFGSEL_SUBNET4 = 0x00000001;
/// Number of IPv6 subnets
static const uint32_t CFGSEL_SUBNET6 = 0x00000002;
/// Number of enabled ifaces
static const uint32_t CFGSEL_IFACE4 = 0x00000004;
/// Number of v6 ifaces
static const uint32_t CFGSEL_IFACE6 = 0x00000008;
/// DDNS enabled/disabled
static const uint32_t CFGSEL_DDNS = 0x00000010;
/// Number of all subnets
static const uint32_t CFGSEL_SUBNET = 0x00000003;
/// IPv4 related config
static const uint32_t CFGSEL_ALL4 = 0x00000015;
/// IPv6 related config
static const uint32_t CFGSEL_ALL6 = 0x0000001A;
/// Whole config
static const uint32_t CFGSEL_ALL = 0xFFFFFFFF;
//@}
/// @brief logging specific information
LoggingInfoStorage logging_info_;
/// @brief Returns summary of the configuration in the textual format.
///
/// This method returns the brief text describing the current configuration.
/// It may be used for logging purposes, e.g. when the new configuration is
/// committed to notify a user about the changes in configuration.
///
/// @todo Currently this method uses @c CfgMgr accessors to get the
/// configuration parameters. Once these parameters are migrated from the
/// @c CfgMgr this method will have to be modified accordingly.
///
/// @todo Implement reporting a summary of interfaces being used for
/// receiving and sending DHCP messages. This will be implemented with
/// ticket #3512.
///
/// @param selection Is a bitfield which describes the parts of the
/// configuration to be returned.
///
/// @return Summary of the configuration in the textual format.
std::string getConfigSummary(const uint32_t selection) const;
};
/// @brief pointer to the configuration
......
......@@ -14,9 +14,12 @@
#include <config.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/configuration.h>
#include <dhcpsrv/subnet.h>
#include <gtest/gtest.h>
using namespace isc::asiolink;
using namespace isc::dhcp;
// Those are the tests for Configuration storage. Right now they are minimal,
......@@ -25,18 +28,138 @@ using namespace isc::dhcp;
namespace {
// Check that by default there are no logging entries
TEST(ConfigurationTest, basic) {
Configuration x;
/// @brief Number of IPv4 and IPv6 subnets to be created for a test.
const int TEST_SUBNETS_NUM = 3;
/// @brief Test fixture class for testing configuration data storage.
class ConfigurationTest : public ::testing::Test {
public:
/// @brief Constructor.
///
/// Creates IPv4 and IPv6 subnets for unit test. The number of subnets
/// is @c TEST_SUBNETS_NUM for IPv4 and IPv6 each.
ConfigurationTest() {
// Remove any subnets dangling from previous unit tests.
clearSubnets();
// Create IPv4 subnets.
for (int i = 0; i < TEST_SUBNETS_NUM; ++i) {
// Default triplet carried undefined value.
Triplet<uint32_t> def_triplet;
// Create a collection of subnets: 192.0.X.0/24 where X is
// 0, 1, 2 etc.
Subnet4Ptr subnet(new Subnet4(IOAddress(0xC0000000 | (i << 2)),
24, def_triplet, def_triplet,
4000));
test_subnets4_.push_back(subnet);
}
// Create IPv6 subnets.
for (int i = 0; i < TEST_SUBNETS_NUM; ++i) {
// This is a base prefix. All other prefixes will be created by
// modifying this one.
IOAddress prefix("2001:db8:1::0");
std::vector<uint8_t> prefix_bytes = prefix.toBytes();
// Modify 5th byte of the prefix, so 2001:db8:1::0 becomes
// 2001:db8:2::0 etc.
++prefix_bytes[5];
prefix = IOAddress::fromBytes(prefix.getFamily(), &prefix_bytes[0]);
Subnet6Ptr subnet(new Subnet6(prefix, 64, 1000, 2000, 3000, 4000));
test_subnets6_.push_back(subnet);
}
}
/// @brief Destructor.
///
/// Removes any dangling configuration.
virtual ~ConfigurationTest() {
clearSubnets();
}
/// @brief Convenience function which adds IPv4 subnet to the configuration.
///
/// @param index Index of the subnet in the @c test_subnets4_ collection
/// which should be added to the configuration. The configuration is stored
/// in the @ conf_ member. This value must be lower than
/// @c TEST_SUBNETS_NUM.
///
/// @todo Until the subnets configuration is migrated from the @c CfgMgr to
/// the @c Configuration object, this function adds the subnet to the
/// @c CfgMgr. Once, the subnet configuration is held in the
/// @c Configuration this function must be modified to store the subnets in
/// the @c conf_ object.
void addSubnet4(const unsigned int index);
/// @brief Convenience function which adds IPv6 subnet to the configuration.
///
/// @param index Index of the subnet in the @c test_subnets6_ collection
/// which should be added to the configuration. The configuration is stored
/// in the @ conf_ member. This value must be lower than
/// @c TEST_SUBNETS_NUM.
///
/// @todo Until the subnets configuration is migrated from the @c CfgMgr to
/// the @c Configuration object, this function adds the subnet to the
/// @c CfgMgr. Once, the subnet configuration is held in the
/// @c Configuration this function must be modified to store the subnets in
/// @c conf_ object.
void addSubnet6(const unsigned int index);
/// @brief Removes all subnets from the configuration.
///
/// @todo Modify this function once the subnet configuration is migrated
/// from @c CfgMgr to @c Configuration.
void clearSubnets();
/// @brief Enable/disable DDNS.
///
/// @param enable A boolean value indicating if the DDNS should be
/// enabled (true) or disabled (false).
void enableDDNS(const bool enable);
/// @brief Stores configuration.
Configuration conf_;
/// @brief A collection of IPv4 subnets used by unit tests.
Subnet4Collection test_subnets4_;
/// @brief A collection of IPv6 subnets used by unit tests.
Subnet6Collection test_subnets6_;
};
void
ConfigurationTest::addSubnet4(const unsigned int index) {
if (index >= TEST_SUBNETS_NUM) {
FAIL() << "Subnet index " << index << "out of range (0.."
<< TEST_SUBNETS_NUM << "): " << "unable to add IPv4 subnet";
}
CfgMgr::instance().addSubnet4(test_subnets4_[index]);
}
EXPECT_TRUE(x.logging_info_.empty());
void
ConfigurationTest::addSubnet6(const unsigned int index) {
if (index >= TEST_SUBNETS_NUM) {
FAIL() << "Subnet index " << index << "out of range (0.."
<< TEST_SUBNETS_NUM << "): " << "unable to add IPv6 subnet";
}
CfgMgr::instance().addSubnet6(test_subnets6_[index]);
}
// Check that Configuration can store logging information.
TEST(ConfigurationTest, loggingInfo) {
void
ConfigurationTest::clearSubnets() {
CfgMgr::instance().deleteSubnets4();
CfgMgr::instance().deleteSubnets6();
}
Configuration x;
void
ConfigurationTest::enableDDNS(const bool enable) {
// D2 configuration should always be non-NULL.
CfgMgr::instance().getD2ClientConfig()->enableUpdates(enable);
}
// Check that by default there are no logging entries
TEST_F(ConfigurationTest, basic) {
EXPECT_TRUE(conf_.logging_info_.empty());
}
// Check that Configuration can store logging information.
TEST_F(ConfigurationTest, loggingInfo) {
LoggingInfo log1;
log1.name_ = "foo";
log1.severity_ = isc::log::WARN;
......@@ -49,15 +172,84 @@ TEST(ConfigurationTest, loggingInfo) {
log1.destinations_.push_back(dest);
x.logging_info_.push_back(log1);
conf_.logging_info_.push_back(log1);
EXPECT_EQ("foo", conf_.logging_info_[0].name_);
EXPECT_EQ(isc::log::WARN, conf_.logging_info_[0].severity_);
EXPECT_EQ(77, conf_.logging_info_[0].debuglevel_);
EXPECT_EQ("some-logfile.txt", conf_.logging_info_[0].destinations_[0].output_);
EXPECT_EQ(5, conf_.logging_info_[0].destinations_[0].maxver_);
EXPECT_EQ(2097152, conf_.logging_info_[0].destinations_[0].maxsize_);
}
EXPECT_EQ("foo", x.logging_info_[0].name_);
EXPECT_EQ(isc::log::WARN, x.logging_info_[0].severity_);
EXPECT_EQ(77, x.logging_info_[0].debuglevel_);
// Check that the configuration summary including information about the status
// of DDNS is returned.
TEST_F(ConfigurationTest, summaryDDNS) {
EXPECT_EQ("DDNS: disabled",
conf_.getConfigSummary(Configuration::CFGSEL_DDNS));
enableDDNS(true);
EXPECT_EQ("DDNS: enabled",
conf_.getConfigSummary(Configuration::CFGSEL_DDNS));
enableDDNS(false);
EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!; DDNS: disabled",
conf_.getConfigSummary(Configuration::CFGSEL_ALL));
}
EXPECT_EQ("some-logfile.txt", x.logging_info_[0].destinations_[0].output_);
EXPECT_EQ(5, x.logging_info_[0].destinations_[0].maxver_);
EXPECT_EQ(2097152, x.logging_info_[0].destinations_[0].maxsize_);
// Check that the configuration summary including information about added
// subnets is returned.
TEST_F(ConfigurationTest, summarySubnets) {
EXPECT_EQ("no config details available",
conf_.getConfigSummary(Configuration::CFGSEL_NONE));
// Initially, there are no subnets added but it should be explicitly
// reported when we query for information about the subnets.
EXPECT_EQ("no IPv4 subnets!; no IPv6 subnets!",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
// If we just want information about IPv4 subnets, there should be no
// mention of IPv6 subnets, even though there are none added.
EXPECT_EQ("no IPv4 subnets!",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET4));
// If we just want information about IPv6 subnets, there should be no
// mention of IPv4 subnets, even though there are none added.
EXPECT_EQ("no IPv6 subnets!",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET6));
// Add IPv4 subnet and make sure it is reported.
addSubnet4(0);
EXPECT_EQ("added IPv4 subnets: 1",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET4));
EXPECT_EQ("added IPv4 subnets: 1; no IPv6 subnets!",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
// Add IPv6 subnet and make sure it is reported.
addSubnet6(0);
EXPECT_EQ("added IPv6 subnets: 1",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET6));
EXPECT_EQ("added IPv4 subnets: 1; added IPv6 subnets: 1",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
// Add one more subnet and make sure the bumped value is only
// for IPv4, but not for IPv6.
addSubnet4(1);
EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 1",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET));
EXPECT_EQ("added IPv4 subnets: 2",
conf_.getConfigSummary(Configuration::CFGSEL_SUBNET4));
addSubnet6(1);
EXPECT_EQ(