KEA_ADMIN_INSTALLED = PREFIX / SBINDIR / 'kea-admin'
KEA_LFC_BUILT = TOP_BUILD_DIR / 'src/bin/lfc/kea-lfc'
KEA_LFC_INSTALLED = PREFIX / SBINDIR / 'kea-lfc'
-LOGDIR = LOCALSTATEDIR / 'log'
+LOGDIR = LOCALSTATEDIR / 'log/kea'
LOGDIR_INSTALLED = PREFIX / LOGDIR
RUNSTATEDIR = LOCALSTATEDIR / 'run/kea'
RUNSTATEDIR_INSTALLED = PREFIX / RUNSTATEDIR
# Set env KEA_HOOKS_PATH to override DEFAULT_HOOKS_PATH
export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/agent/tests/@dotlibs@"
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/agent/tests"
+
# Control Agent configuration to be stored in the configuration file.
CONFIG="{
\"Control-agent\":
# D2 configuration to be stored in the configuration file.
# Set env KEA_HOOKS_PATH to override DEFAULT_HOOKS_PATH
export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/d2/tests/@dotlibs@"
+
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/d2/tests"
+
CONFIG="{
\"DhcpDdns\":
{
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <hooks/hooks_manager.h>
#include <log/logger_support.h>
+#include <process/log_parser.h>
#include <stats/stats_mgr.h>
#include <util/multi_threading_mgr.h>
#include <util/chrono_time_utils.h>
using namespace isc::stats;
using namespace isc::test;
using namespace isc::util;
+using namespace isc::process;
namespace ph = std::placeholders;
namespace {
///
/// Sets socket path to its default value.
CtrlChannelDhcpv4SrvTest() : interfaces_("\"*\"") {
+ resetLogPath();
const char* env = getenv("KEA_SOCKET_TEST_DIR");
if (env) {
socket_path_ = string(env) + "/kea4.sock";
/// @brief Destructor
~CtrlChannelDhcpv4SrvTest() {
+ resetLogPath();
if (test_timer_) {
test_timer_->cancel();
getIOService()->stopAndPoll();
IfaceMgr::instance().detectIfaces();
}
+ /// @brief Sets the log path where log output may be written.
+ /// @param explicit_path path to use as the log path.
+ void setLogTestPath(const std::string explicit_path = "") {
+ LogConfigParser::getLogPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+ }
+
+ /// @brief Resets the log path to TEST_DATA_BUILDDIR.
+ void resetLogPath() {
+ LogConfigParser::getLogPath(true);
+ }
+
/// @brief Returns pointer to the server's IO service.
///
/// @return Pointer to the server's IO service or null pointer if the server
// Check that the "config-set" command will replace current configuration
TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
+ setLogTestPath("/dev");
createUnixChannelServer();
// Define strings to permutate the config arguments
// Verify that the "config-test" command will do what we expect.
TEST_F(CtrlChannelDhcpv4SrvTest, configTest) {
+ setLogTestPath("/dev");
createUnixChannelServer();
// Define strings to permutate the config arguments
# Set env KEA_HOOKS_PATH to override DEFAULT_HOOKS_PATH
export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/dhcp4/tests/@dotlibs@"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/dhcp4/tests"
+
# Kea configuration to be stored in the configuration file.
CONFIG="{
\"Dhcp4\":
#include <http/response_parser.h>
#include <http/testutils/test_http_client.h>
#include <log/logger_support.h>
+#include <process/log_parser.h>
#include <stats/stats_mgr.h>
#include <util/chrono_time_utils.h>
using namespace isc::http;
using namespace isc::stats;
using namespace isc::util;
+using namespace isc::process;
namespace ph = std::placeholders;
namespace {
IfaceMgr::instance().setTestMode(false);
IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
IfaceMgr::instancePtr().get(), ph::_1));
+ setLogTestPath("/dev");
}
/// @brief Destructor
IfaceMgr::instance().clearIfaces();
IfaceMgr::instance().closeSockets();
IfaceMgr::instance().detectIfaces();
+ resetLogPath();
+ }
+
+ /// @brief Sets the log path where log output may be written.
+ /// @param explicit_path path to use as the log path.
+ void setLogTestPath(const std::string explicit_path = "") {
+ LogConfigParser::getLogPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+ }
+
+ /// @brief Resets the log path to TEST_DATA_BUILDDIR.
+ void resetLogPath() {
+ LogConfigParser::getLogPath(true);
}
/// @brief Returns pointer to the server's IO service.
// Check that the "config-set" command will replace current configuration
TEST_F(CtrlChannelDhcpv6SrvTest, configSet) {
+ setLogTestPath("/dev");
createUnixChannelServer();
// Define strings to permutate the config arguments
// Verify that the "config-test" command will do what we expect.
TEST_F(CtrlChannelDhcpv6SrvTest, configTest) {
+ setLogTestPath("/dev");
createUnixChannelServer();
// Define strings to permutate the config arguments
# Set env KEA_HOOKS_PATH to override DEFAULT_HOOKS_PATH
export KEA_HOOKS_PATH="@abs_top_builddir@/src/bin/dhcp6/tests/@dotlibs@"
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/dhcp6/tests"
+
# Kea configuration to be stored in the configuration file.
CONFIG="{
\"Dhcp6\":
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/json_config_parser.h>
#include <log/logger_support.h>
+#include <process/log_parser.h>
#include <stats/stats_mgr.h>
#include <util/pointer_util.h>
#include <cstdio>
using namespace isc::asiolink;
using namespace isc::stats;
using namespace isc::util;
+using namespace isc::process;
using namespace boost::posix_time;
namespace isc {
CfgMgr::instance().setFamily(AF_INET6);
original_datadir_ = CfgMgr::instance().getDataDir();
CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR);
+ resetLogPath();
}
BaseServerTest::~BaseServerTest() {
// Revert to unit test logging, in case the test reconfigured it.
isc::log::initLogger();
+ resetLogPath();
+}
+
+void
+BaseServerTest::setLogTestPath(const std::string explicit_path /* = "" */) {
+ LogConfigParser::getLogPath(true, (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+}
+
+void
+BaseServerTest::resetLogPath() {
+ LogConfigParser::getLogPath(true);
}
Dhcpv6SrvTest::Dhcpv6SrvTest()
/// @brief Destructor.
virtual ~BaseServerTest();
+ /// @brief Sets the log path where log output may be written.
+ /// @param explicit_path path to use as the log path.
+ void setLogTestPath(const std::string explicit_path = "");
+
+ /// @brief Resets the log path to TEST_DATA_BUILDDIR.
+ void resetLogPath();
+
private:
/// @brief Holds the original data directory.
HttpCtrlDhcpv6Test()
: BaseServerTest() {
reset();
+ setLogTestPath("/dev");
}
virtual ~HttpCtrlDhcpv6Test() {
KEACTRL_CFG_FILE="@abs_top_builddir@/src/bin/keactrl/tests/keactrl_test.conf"
# Path to the Kea log file.
LOG_FILE="@abs_top_builddir@/src/bin/keactrl/tests/test.log"
+
+# Set env KEA_LOG_FILE_DIR to override default log path
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/keactrl/tests"
+
# Binaries' names
wildcard_name="kea-"
kea4_name="${wildcard_name}dhcp4"
# Path to the Control Agent log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Control Agent configuration to be stored in the configuration file.
# todo: use actual configuration once we support it.
CONFIG="{
# Path to the D2 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# D2 Server configuration to be stored in the configuration file.
# todo: use actual configuration once we support it.
CONFIG="{
# Path to the Dhcp4 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Dhcp4 Server configuration to be stored in the configuration file.
# todo: use actual configuration once we support it.
CONFIG="{
# Path to the Dhcp6 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Dhcp6 Server configuration to be stored in the configuration file.
# todo: use actual configuration once we support it.
CONFIG="{
# Path to the Control Agent log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Control Agent configuration to be stored in the configuration file.
# todo: use actual configuration once we support it.
CONFIG="{
# Path to the D2 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# D2 Server configuration to be stored in the configuration file.
# todo: use actual configuration once we support it.
CONFIG="{
# Path to the Control Agent log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Path to the test certificate authority directory.
TEST_CA_DIR="@abs_top_srcdir@/src/lib/asiolink/testutils/ca"
# Path to the D2 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Path to the test certificate authority directory.
TEST_CA_DIR="@abs_top_srcdir@/src/lib/asiolink/testutils/ca"
# Path to the Dhcp4 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Path to the test certificate authority directory.
TEST_CA_DIR="@abs_top_srcdir@/src/lib/asiolink/testutils/ca"
# Path to the Dhcp6 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"
+# Set env KEA_LOG_FILE_DIR to override default log path.
+export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"
+
# Path to the test certificate authority directory.
TEST_CA_DIR="@abs_top_srcdir@/src/lib/asiolink/testutils/ca"
#include <log/logger_support.h>
#include <log/logger_manager.h>
#include <log/logger_name.h>
+#include <util/filesystem.h>
using namespace isc::data;
using namespace isc::log;
+using namespace isc::util::file;
namespace isc {
namespace process {
+namespace {
+ // Singleton PathChecker to set and hold valid log file path.
+ PathCheckerPtr log_path_checker_;
+};
+
LogConfigParser::LogConfigParser(const ConfigPtr& storage)
:config_(storage), verbose_(false) {
if (!storage) {
config_->addLoggingInfo(info);
}
+std::string
+LogConfigParser::getLogPath(bool reset /* = false */, const std::string explicit_path /* = "" */) {
+ if (!log_path_checker_ || reset) {
+ log_path_checker_.reset(new PathChecker(LOGFILE_DIR, "KEA_LOG_FILE_DIR"));
+ if (!explicit_path.empty()) {
+ log_path_checker_->getPath(true, explicit_path);
+ }
+ }
+
+ return (log_path_checker_->getPath());
+}
+
+std::string
+LogConfigParser::validatePath(const std::string logpath,
+ bool enforce_path /* = true */) {
+ if (!log_path_checker_) {
+ getLogPath();
+ }
+
+ return (log_path_checker_->validatePath(logpath, enforce_path));
+}
+
+
void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destination,
isc::data::ConstElementPtr output_options) {
if (!output_options) {
isc_throw(BadValue, "output-options entry does not have a mandatory 'output' "
"element (" << output_option->getPosition() << ")");
}
- dest.output_ = output->stringValue();
+
+ auto output_str = output->stringValue();
+ if ((output_str == "stdout") ||
+ (output_str == "stderr") ||
+ (output_str == "syslog")) {
+ dest.output_ = output_str;
+ } else {
+ try {
+ dest.output_ = validatePath(output_str);
+ } catch (const std::exception& ex) {
+ isc_throw(BadValue, "invalid path in `output`: " << ex.what()
+ << " (" << output_option->getPosition() << ")");
+ }
+ }
isc::data::ConstElementPtr maxver_ptr = output_option->get("maxver");
if (maxver_ptr) {
void parseConfiguration(const isc::data::ConstElementPtr& log_config,
bool verbose = false);
+ /// @brief Fetches the supported log file path.
+ ///
+ /// The first call to this function with no arguments will set the default
+ /// hooks path to either the value of LOGFILE_DIR or the environment
+ /// variable KEA_LOG_FILE_DIR if it is defined. Subsequent calls with no
+ /// arguments will simply return this value.
+ ///
+ /// @param reset recalculate when true, defaults to false.
+ /// @param explicit_path set default log path to this value. This is
+ /// for testing purposes only.
+ ///
+ /// @return String containing the default log file path.
+ static std::string getLogPath(bool reset = false, const std::string explicit_path = "");
+
+ /// @brief Validates a library path against the supported path for log files.
+ ///
+ /// @param logpath path to validate.
+ /// @param enforce_path enables validation against the supported path.
+ /// If false verifies only that the path contains a file name.
+ ///
+ /// @return validated path
+ static std::string validatePath(const std::string logpath, bool enforce_path = true);
+
private:
/// @brief Parses one JSON structure in Server/loggers" array
'log_parser.cc',
'process_messages.cc',
'redact_config.cc',
- cpp_args: [f'-DPIDFILE_DIR="@RUNSTATEDIR_INSTALLED@"'],
+ cpp_args: [
+ f'-DPIDFILE_DIR="@RUNSTATEDIR_INSTALLED@"',
+ f'-DLOGFILE_DIR="@LOGDIR_INSTALLED@"'
+ ],
include_directories: [include_directories('.')] + INCLUDES,
install: true,
install_dir: LIBDIR,
class LoggingTest : public ::testing::Test {
public:
/// @brief Constructor
- LoggingTest() {}
+ LoggingTest() {
+ resetLogPath();
+ }
/// @brief Destructor
///
~LoggingTest() {
isc::log::initLogger();
wipeFiles();
+ resetLogPath();
}
/// @brief Generates a log file name suffixed with a rotation number
static_cast<void>(remove(os.str().c_str()));
}
+ /// @brief Sets the Hooks path from which hooks can be loaded.
+ /// @param explicit_path path to use as the hooks path.
+ void setLogTestPath(const std::string explicit_path = "") {
+ LogConfigParser::getLogPath(true,
+ (!explicit_path.empty() ?
+ explicit_path : TEST_DATA_BUILDDIR));
+ }
+
+ /// @brief Resets the log path to default.
+ void resetLogPath() {
+ LogConfigParser::getLogPath(true);
+ }
+
/// @brief Name of the log file
static const char* TEST_LOG_NAME;
// We need to parse properly formed JSON and then extract
// "loggers" element from it. For some reason fromJSON is
- // throwing at opening square bracket
+ // throwing at openin0 square bracket
ConstElementPtr config = Element::fromJSON(config_txt);
config = config->get("loggers");
EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
- EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
+ EXPECT_EQ(LogConfigParser::validatePath("logfile.txt"),
+ storage->getLoggingInfo()[0].destinations_[0].output_);
// Default for immediate flush is true
EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_);
EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_);
EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
- EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
+ EXPECT_EQ(LogConfigParser::validatePath("logfile.txt"),
+ storage->getLoggingInfo()[0].destinations_[0].output_);
EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_);
-
EXPECT_EQ("wombat", storage->getLoggingInfo()[1].name_);
EXPECT_EQ(99, storage->getLoggingInfo()[1].debuglevel_);
EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[1].severity_);
ASSERT_EQ(1, storage->getLoggingInfo()[1].destinations_.size());
- EXPECT_EQ("logfile2.txt" , storage->getLoggingInfo()[1].destinations_[0].output_);
+ EXPECT_EQ(LogConfigParser::validatePath("logfile2.txt"),
+ storage->getLoggingInfo()[1].destinations_[0].output_);
EXPECT_FALSE(storage->getLoggingInfo()[1].destinations_[0].flush_);
}
// into Configuration usable by log4cplus. This test checks that more than
// one logging destination can be configured.
TEST_F(LoggingTest, multipleLoggingDestinations) {
-
+ setLogTestPath();
const char* config_txt =
"{ \"loggers\": ["
" {"
EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_);
EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
ASSERT_EQ(2, storage->getLoggingInfo()[0].destinations_.size());
- EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_);
+ EXPECT_EQ(LogConfigParser::validatePath("logfile.txt"),
+ storage->getLoggingInfo()[0].destinations_[0].output_);
EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_);
EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[1].output_);
EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[1].flush_);
// we can correctly configure logging such that rotation occurs as
// expected.
TEST_F(LoggingTest, logRotate) {
+ setLogTestPath();
wipeFiles();
std::ostringstream os;
EXPECT_EQ(TEST_MAX_VERS, server_cfg->getLoggingInfo()[0].destinations_[0].maxver_);
// Make sure we have the initial log file.
- ASSERT_TRUE(isc::test::fileExists(TEST_LOG_NAME));
+ ASSERT_TRUE(isc::test::fileExists(LogConfigParser::validatePath(TEST_LOG_NAME)));
// Now generate a log we know will be large enough to force a rotation.
// We borrow a one argument log message for the test.
for (int i = 1; i < TEST_MAX_VERS + 1; i++) {
// Output the big log and make sure we get the expected rotation file.
LOG_INFO(logger, DCTL_CONFIG_COMPLETE).arg(big_arg);
- EXPECT_TRUE(isc::test::fileExists(logName(i).c_str()));
+ EXPECT_TRUE(isc::test::fileExists(LogConfigParser::validatePath(logName(i).c_str())));
}
// Clean up.
// Test that maxsize can be configured with high values.
TEST_F(LoggingTest, maxsize) {
+ setLogTestPath();
testMaxSize(TEST_MAX_SIZE, TEST_MAX_SIZE);
testMaxSize(std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max());
testMaxSize(std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max());