Commit a1f12960 authored by Stephen Morris's avatar Stephen Morris
Browse files

[2364] Merge branch 'master' into trac2364

parents 4a1003b5 0811abc7
511. [bug] stephen
Fixed a race condition in the DHCP tests whereby the test program
spawned a subprocess and attempted to read (without waiting) from
the interconnecting pipe before the subprocess had written
anything. The lack of output was being interpreted as a test
failure.
(Trac #2410, git f53e65cdceeb8e6da4723730e4ed0a17e4646579)
510. [func] marcin
DHCP option instances can be created using a collection of strings.
Each string represents a value of a particular data field within
an option. The data field values, given as strings, are validated
against the actual types of option fields specified in the options
definitions.
(Trac #2490, git 56cfd6612fcaeae9acec4a94e1e5f1a88142c44d)
509. [func] muks
Log messages now include the pid of the process that logged the
message.
(Trac #1745, git fc8bbf3d438e8154e7c2bdd322145a7f7854dc6a)
508. [bug] stephen
Split the DHCP library into two directories, each with its own
Makefile. This properly solves the problem whereby a "make"
......
......@@ -20,6 +20,13 @@ dist_doc_DATA = AUTHORS COPYING ChangeLog README
.PHONY: check-valgrind check-valgrind-suppress
install-exec-hook:
-@echo -e "\033[1;33m" # switch to yellow color text
@echo "NOTE: BIND 10 does not automatically start DNS services when it is run"
@echo " in its default configuration. Please see the Guide for information"
@echo " on how to configure these services to be started automatically."
-@echo -e "\033[m" # switch back to normal
check-valgrind:
if HAVE_VALGRIND
@VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --track-origins=yes --num-callers=48 --leak-check=full --fullpath-after=" \
......
......@@ -58,5 +58,7 @@ For operating system specific tips see the wiki at:
Please see the wiki and the doc/ directory for various documentation.
The BIND 10 suite is started by running "bind10". Note that the
default configuration does not run any DNS or DHCP servers.
The BIND 10 suite is started by running "bind10". Note that the default
configuration does not start any DNS or DHCP services. Please see the
Guide for information on how to configure these services to be started
automatically.
......@@ -425,11 +425,12 @@ var/
</listitem>
<listitem>
<para>In another console, enable the authoritative DNS service
(by using the <command>bindctl</command> utility to configure
the <command>b10-auth</command> component to run):
<screen>$ <userinput>bindctl</userinput></screen>
(Login with the provided default username and password.)
<para>DNS and DHCP components are not started in the default
configuration. In another console, enable the authoritative
DNS service (by using the <command>bindctl</command> utility
to configure the <command>b10-auth</command> component to
run): <screen>$ <userinput>bindctl</userinput></screen>
(Login with the provided default username and password.)
<screen>
&gt; <userinput>config add Boss/components b10-auth</userinput>
&gt; <userinput>config set Boss/components/b10-auth/special auth</userinput>
......
......@@ -45,11 +45,30 @@ class TestDhcpv4Daemon(unittest.TestCase):
def tearDown(self):
pass
def readPipe(self, pipe_fd):
"""
Reads bytes from a pipe and returns a character string. If nothing is
read, or if there is an error, an empty string is returned.
pipe_fd - Pipe file descriptor to read
"""
try:
data = os.read(pipe_fd, 16384)
# Make sure we have a string
if (data is None):
data = ""
else:
data = str(data)
except OSError:
data = ""
return data
def runCommand(self, params, wait=1):
"""
This method runs dhcp4 and returns a tuple: (returncode, stdout, stderr)
This method runs a command and returns a tuple: (returncode, stdout, stderr)
"""
## @todo: Convert this into generic method and reuse it in dhcp6
## @todo: Convert this into generic method and reuse it in dhcp4 and dhcp6
print("Running command: %s" % (" ".join(params)))
......@@ -89,46 +108,48 @@ class TestDhcpv4Daemon(unittest.TestCase):
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
# There's potential problem if b10-dhcp4 prints out more
# than 16kB of text
try:
output = os.read(self.stdout_pipes[0], 16384)
except OSError:
print("No data available from stdout")
output = ""
# read can return None. Make sure we have a string
if (output is None):
output = ""
try:
error = os.read(self.stderr_pipes[0], 16384)
except OSError:
print("No data available on stderr")
error = ""
# read can return None. Make sure we have a string
if (error is None):
error = ""
try:
if (not pi.process.poll()):
# let's be nice at first...
# As we don't know how long the subprocess will take to start and
# produce output, we'll loop and sleep for 250 ms between each
# iteration. To avoid an infinite loop, we'll loop for a maximum
# of five seconds: that should be enough.
for count in range(20):
# Read something from stderr and stdout (these reads don't block).
output = self.readPipe(self.stdout_pipes[0])
error = self.readPipe(self.stderr_pipes[0])
# If the process has already exited, or if it has output something,
# quit the loop now.
if pi.process.poll() is not None or len(error) > 0 or len(output) > 0:
break
# Process still running, try again in 250 ms.
time.sleep(0.25)
# Exited loop, kill the process if it is still running
if pi.process.poll() is None:
try:
pi.process.terminate()
except OSError:
print("Ignoring failed kill attempt. Process is dead already.")
except OSError:
print("Ignoring failed kill attempt. Process is dead already.")
# call this to get returncode, process should be dead by now
rc = pi.process.wait()
# Clean up our stdout/stderr munging.
os.dup2(self.stdout_old, sys.stdout.fileno())
os.close(self.stdout_old)
os.close(self.stdout_pipes[0])
os.dup2(self.stderr_old, sys.stderr.fileno())
os.close(self.stderr_old)
os.close(self.stderr_pipes[0])
# Free up resources (file descriptors) from the ProcessInfo object
# TODO: For some reason, this gives an error if the process has ended,
# although it does cause all descriptors still allocated to the
# object to be freed.
pi = None
print ("Process finished, return code=%d, stdout=%d bytes, stderr=%d bytes"
% (rc, len(output), len(error)) )
......
......@@ -698,11 +698,8 @@ private:
// We have exactly one option definition for the particular option code
// use it to create the option instance.
const OptionDefinitionPtr& def = *(range.first);
// getFactory should never return NULL pointer.
Option::Factory* factory = def->getFactory();
assert(factory != NULL);
try {
OptionPtr option = factory(Option::V6, option_code, binary);
OptionPtr option = def->optionFactory(Option::V6, option_code, binary);
Subnet::OptionDescriptor desc(option, false);
option_descriptor_.option = option;
option_descriptor_.persistent = false;
......
......@@ -56,12 +56,6 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
// Initialize objects required for DHCP server operation.
try {
// Initialize standard DHCPv6 option definitions. This function
// may throw bad_alloc if system goes out of memory during the
// creation if option definitions. It may also throw isc::Unexpected
// if definitions are wrong. This would mean error in implementation.
initStdOptionDefs();
// Port 0 is used for testing purposes. It means that the server should
// not open any sockets at all. Some tests, e.g. configuration parser,
// require Dhcpv6Srv object, but they don't really need it to do
......@@ -622,10 +616,5 @@ Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
return (UNKNOWN);
}
void
Dhcpv6Srv::initStdOptionDefs() {
LibDHCP::initStdOptionDefs(Option::V6);
}
};
};
......@@ -241,17 +241,6 @@ protected:
/// interfaces for new DUID generation are detected.
void setServerID();
/// @brief Initializes option definitions for standard options.
///
/// Each standard option's format is described by the
/// dhcp::OptionDefinition object. This function creates such objects
/// for each standard DHCPv6 option.
///
/// @todo list thrown exceptions.
/// @todo extend this function to cover all standard options. Currently
/// it is limited to critical options only.
void initStdOptionDefs();
private:
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
......
......@@ -46,11 +46,6 @@ public:
// srv_(0) means to not open any sockets. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
// Create instances of option definitions and put them into storage.
// This is normally initialized by the server when calling run()
// run() function.
LibDHCP::initStdOptionDefs(Option::V6);
}
~Dhcp6ParserTest() {
......
......@@ -45,6 +45,25 @@ class TestDhcpv6Daemon(unittest.TestCase):
def tearDown(self):
pass
def readPipe(self, pipe_fd):
"""
Reads bytes from a pipe and returns a character string. If nothing is
read, or if there is an error, an empty string is returned.
pipe_fd - Pipe file descriptor to read
"""
try:
data = os.read(pipe_fd, 16384)
# Make sure we have a string
if (data is None):
data = ""
else:
data = str(data)
except OSError:
data = ""
return data
def runCommand(self, params, wait=1):
"""
This method runs a command and returns a tuple: (returncode, stdout, stderr)
......@@ -89,45 +108,48 @@ class TestDhcpv6Daemon(unittest.TestCase):
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
# There's potential problem if b10-dhcp4 prints out more
# than 16k of text
try:
output = os.read(self.stdout_pipes[0], 16384)
except OSError:
print("No data available from stdout")
output = ""
# read can return None. Make sure we have a string
if (output is None):
output = ""
try:
error = os.read(self.stderr_pipes[0], 16384)
except OSError:
print("No data available on stderr")
error = ""
# read can return None. Make sure we have a string
if (error is None):
error = ""
try:
if (not pi.process.poll()):
# let's be nice at first...
# As we don't know how long the subprocess will take to start and
# produce output, we'll loop and sleep for 250 ms between each
# iteration. To avoid an infinite loop, we'll loop for a maximum
# of five seconds: that should be enough.
for count in range(20):
# Read something from stderr and stdout (these reads don't block).
output = self.readPipe(self.stdout_pipes[0])
error = self.readPipe(self.stderr_pipes[0])
# If the process has already exited, or if it has output something,
# quit the loop now.
if pi.process.poll() is not None or len(error) > 0 or len(output) > 0:
break
# Process still running, try again in 250 ms.
time.sleep(0.25)
# Exited loop, kill the process if it is still running
if pi.process.poll() is None:
try:
pi.process.terminate()
except OSError:
print("Ignoring failed kill attempt. Process is dead already.")
except OSError:
print("Ignoring failed kill attempt. Process is dead already.")
# call this to get returncode, process should be dead by now
rc = pi.process.wait()
# Clean up our stdout/stderr munging.
os.dup2(self.stdout_old, sys.stdout.fileno())
os.close(self.stdout_old)
os.close(self.stdout_pipes[0])
os.dup2(self.stderr_old, sys.stderr.fileno())
os.close(self.stderr_old)
os.close(self.stderr_pipes[0])
# Free up resources (file descriptors) from the ProcessInfo object
# TODO: For some reason, this gives an error if the process has ended,
# although it does cause all descriptors still allocated to the
# object to be freed.
pi = None
print ("Process finished, return code=%d, stdout=%d bytes, stderr=%d bytes"
% (rc, len(output), len(error)) )
......
......@@ -61,7 +61,7 @@ IOAddress::toText() const {
}
IOAddress
IOAddress::from_bytes(short family, const uint8_t* data) {
IOAddress::fromBytes(short family, const uint8_t* data) {
if (data == NULL) {
isc_throw(BadValue, "NULL pointer received.");
} else
......
......@@ -111,7 +111,7 @@ public:
///
/// \return Created IOAddress object
static IOAddress
from_bytes(short family, const uint8_t* data);
fromBytes(short family, const uint8_t* data);
/// \brief Compare addresses for equality
///
......
......@@ -64,7 +64,7 @@ TEST(IOAddressTest, Family) {
EXPECT_EQ(AF_INET6, IOAddress("2001:0DB8:0:0::0012").getFamily());
}
TEST(IOAddressTest, from_bytes) {
TEST(IOAddressTest, fromBytes) {
// 2001:db8:1::dead:beef
uint8_t v6[] = {
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0,
......@@ -74,12 +74,12 @@ TEST(IOAddressTest, from_bytes) {
IOAddress addr("::");
EXPECT_NO_THROW({
addr = IOAddress::from_bytes(AF_INET6, v6);
addr = IOAddress::fromBytes(AF_INET6, v6);
});
EXPECT_EQ("2001:db8:1::dead:beef", addr.toText());
EXPECT_NO_THROW({
addr = IOAddress::from_bytes(AF_INET, v4);
addr = IOAddress::fromBytes(AF_INET, v4);
});
EXPECT_EQ(addr.toText(), IOAddress("192.0.2.3").toText());
}
......
......@@ -36,6 +36,8 @@ libb10_datasrc_la_SOURCES += database.h database.cc
libb10_datasrc_la_SOURCES += factory.h factory.cc
libb10_datasrc_la_SOURCES += client_list.h client_list.cc
libb10_datasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
libb10_datasrc_la_SOURCES += master_loader_callbacks.h
libb10_datasrc_la_SOURCES += master_loader_callbacks.cc
nodist_libb10_datasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
libb10_datasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
......
......@@ -315,6 +315,18 @@ An error was found in the zone data when it was being loaded from
another data source. The zone was not loaded. The specific error is
shown in the message, and should be addressed.
% DATASRC_MASTER_LOAD_ERROR %1:%2: Zone '%3/%4' contains error: %5
There's an error in the given master file. The zone won't be loaded for
this reason. Parsing might follow, so you might get further errors and
warnings to fix everything at once. But in case the error is serious enough,
the parser might just give up or get confused and generate false errors
afterwards.
% DATASRC_MASTER_LOAD_WARN %1:%2: Zone '%3/%4' has a potential problem: %5
There's something suspicious in the master file. This is a warning only.
It may be a problem or it may be harmless, but it should be checked.
This problem does not stop the zone from being loaded.
% DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
Debug information. An RRset is being added to the in-memory data source.
......
// Copyright (C) 2012 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 <datasrc/master_loader_callbacks.h>
#include <datasrc/zone.h>
#include <datasrc/logger.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrset.h>
#include <string>
#include <boost/bind.hpp>
namespace isc {
namespace datasrc {
namespace {
void
logError(const isc::dns::Name& name, const isc::dns::RRClass& rrclass,
bool* ok, const std::string& source, size_t line,
const std::string& reason)
{
LOG_ERROR(logger, DATASRC_MASTER_LOAD_ERROR).arg(source).arg(line).
arg(name).arg(rrclass).arg(reason);
if (ok != NULL) {
*ok = false;
}
}
void
logWarning(const isc::dns::Name& name, const isc::dns::RRClass& rrclass,
const std::string& source, size_t line, const std::string& reason)
{
LOG_WARN(logger, DATASRC_MASTER_LOAD_WARN).arg(source).arg(line).
arg(name).arg(rrclass).arg(reason);
}
}
isc::dns::MasterLoaderCallbacks
createMasterLoaderCallbacks(const isc::dns::Name& name,
const isc::dns::RRClass& rrclass, bool* ok)
{
return (isc::dns::MasterLoaderCallbacks(boost::bind(&logError, name,
rrclass, ok, _1, _2,
_3),
boost::bind(&logWarning, name,
rrclass, _1, _2, _3)));
}
isc::dns::AddRRsetCallback
createMasterLoaderAddCallback(ZoneUpdater& updater) {
return (boost::bind(&ZoneUpdater::addRRset, &updater,
// The callback provides a shared pointer, we
// need the object. This bind unpacks the object.
boost::bind(&isc::dns::RRsetPtr::operator*, _1)));
}
}
}
// Copyright (C) 2012 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.
#ifndef DATASRC_MASTER_LOADER_CALLBACKS_H
#define DATASRC_MASTER_LOADER_CALLBACKS_H
#include <dns/master_loader_callbacks.h>
namespace isc {
namespace dns {
class Name;
class RRClass;
}
namespace datasrc {
class ZoneUpdater;
/// \brief Create issue callbacks for MasterLoader
///
/// This will create set of callbacks for the MasterLoader that
/// will be used to report any issues found in the zone data.
///
/// \param name Name of the zone. Used in logging.
/// \param rrclass The class of the zone. Used in logging.
/// \param ok If this is non-NULL and there are any errors during
/// the loading, it is set to false. Otherwise, it is untouched.
/// \return Set of callbacks to be passed to the master loader.
/// \throw std::bad_alloc when allocation fails.
isc::dns::MasterLoaderCallbacks
createMasterLoaderCallbacks(const isc::dns::Name& name,
const isc::dns::RRClass& rrclass, bool* ok);
/// \brief Create a callback for MasterLoader to add RRsets.
///
/// This creates a callback that can be used by the MasterLoader to add
/// loaded RRsets into a zone updater.
///
/// The zone updater should be opened in the replace mode no changes should
/// have been done to it yet (but it is not checked). It is not commited
/// automatically and it is up to the caller to commit the changes (or not).
/// It must not be destroyed for the whole time of loading.
///
/// The function is mostly straightforward packing of the updater.addRRset
/// into a boost::function, it is defined explicitly due to small technical
/// annoyences around boost::bind application, so it can be reused.
///
/// \param updater The zone updater to use.
/// \return The callback to be passed to MasterLoader.
/// \throw std::bad_alloc when allocation fails.
isc::dns::AddRRsetCallback
createMasterLoaderAddCallback(ZoneUpdater& updater);
}
}
#endif
......@@ -59,6 +59,7 @@ run_unittests_SOURCES += zonetable_unittest.cc
run_unittests_SOURCES += zone_finder_context_unittest.cc
run_unittests_SOURCES += faked_nsec3.h faked_nsec3.cc
run_unittests_SOURCES += client_list_unittest.cc
run_unittests_SOURCES += master_loader_callbacks_test.cc
# We need the actual module implementation in the tests (they are not part
# of libdatasrc)
......
// Copyright (C) 2012 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 <datasrc/master_loader_callbacks.h>
#include <datasrc/zone.h>
#include <dns/rrset.h>
#include <dns/rrclass.h>
#include <dns/rrttl.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
#include <list>
#include <string>
using namespace isc::datasrc;
namespace {
// An updater for the tests. Most of the virtual methods throw
// NotImplemented, as they are not used in the tests.