mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 05:27:55 +00:00
Merge branch 'master' into trac2198_2
This commit is contained in:
commit
f8181bb722
75
ChangeLog
75
ChangeLog
@ -1,7 +1,80 @@
|
||||
494. [bug] jinmei
|
||||
Fixed a problem that shutting down BIND 10 kept some of the
|
||||
processes alive. It was two-fold: when the main bind10 process
|
||||
started as a root, started b10-sockcreator with the privilege, and
|
||||
then dropped the privilege, the bind10 process cannot kill the
|
||||
sockcreator via signal any more (when it has to), but it kept
|
||||
sending the signal and didn't stop. Also, when running on Python
|
||||
3.1 (or older), the sockcreator had some additional file
|
||||
descriptor open, which prevented it from exiting even after the
|
||||
bind10 process terminated. Now the bind10 process simply gives up
|
||||
killing a subprocess if it fails due to lack of permission, and it
|
||||
makes sure the socket creator is spawned without any unnecessary
|
||||
FDs open.
|
||||
(Trac #1858, git 405d85c8a0042ba807a3a123611ff383c4081ee1)
|
||||
|
||||
493. [build] jinmei
|
||||
Fixed build failure with newer versions of clang++. These
|
||||
versions are stricter regarding "unused variable" and "unused
|
||||
(driver) arguments" warnings, and cause fatal build error
|
||||
with -Werror. The affected versions of clang++ include Apple's
|
||||
customized version 4.1 included in Xcode 4.5.1. So this fix
|
||||
will solve build errors for Mac OS X that uses newer versions of
|
||||
Xcode.
|
||||
(Trac #2340, git 55be177fc4f7537143ab6ef5a728bd44bdf9d783,
|
||||
3e2a372012e633d017a97029d13894e743199741 and commits before it
|
||||
with [2340] in the commit log)
|
||||
|
||||
492. [func] tomek
|
||||
libdhcpsrv: The DHCP Configuration Manager is now able to store
|
||||
information about IPv4 subnets and pools. It is still not possible
|
||||
to configure that information. Such capability will be implemented
|
||||
in a near future.
|
||||
(Trac #2237, git a78e560343b41f0f692c7903c938b2b2b24bf56b)
|
||||
|
||||
491. [func] tomek
|
||||
b10-dhcp6: Configuration for DHCPv6 has been implemented.
|
||||
Currently it is possible to configure IPv6 subnets and pools
|
||||
within those subnets, global and per subnet values of renew,
|
||||
rebind, preferred and valid lifetimes. Configured parameters
|
||||
are accepted, but are not used yet by the allocation engine yet.
|
||||
(Trac #2269, git 028bed9014b15facf1a29d3d4a822c9d14fc6411)
|
||||
|
||||
490. [func] tomek
|
||||
libdhcpsrv: An abstract API for lease database has been
|
||||
implemented. It offers a common interface to all concrete
|
||||
database backends.
|
||||
(Trac #2140, git df196f7609757253c4f2f918cd91012bb3af1163)
|
||||
|
||||
489. [func] muks
|
||||
The isc::dns::RRsetList class has been removed. It was now unused
|
||||
inside the BIND 10 codebase, and the interface was considered
|
||||
prone to misuse.
|
||||
(Trac #2266, git 532ac3d0054f6a11b91ee369964f3a84dabc6040)
|
||||
|
||||
488. [build] jinmei
|
||||
On configure, changed the search order for Python executable.
|
||||
It first ties more specific file names such as "python3.2" before
|
||||
more generic "python3". This will prevent configure failure on
|
||||
Mac OS X that installs Python3 via recent versions of Homebrew.
|
||||
(Trac #2339, git 88db890d8d1c64de49be87f03c24a2021bcf63da)
|
||||
|
||||
487. [bug] jinmei
|
||||
The bind10 process now terminates a component (subprocess) by the
|
||||
"config remove Boss/components" bindctl command even if the
|
||||
process crashes immediately before the command is sent to bind10.
|
||||
Previously this led to an inconsistent state between the
|
||||
configuration and an internal component list of bind10, and bind10
|
||||
kept trying to restart the component. A known specific case of
|
||||
this problem is that b10-ddns could keep failing (due to lack of
|
||||
dependency modules) and the administrator couldn't stop the
|
||||
restart via bindctl.
|
||||
(Trac #2244, git 7565788d06f216ab254008ffdfae16678bcd00e5)
|
||||
|
||||
486. [bug]* jinmei
|
||||
All public header files for libb10-dns++ are now installed.
|
||||
Template configure.ac and utility AC macros for external projects
|
||||
using the library is provided under the "examples" directory.
|
||||
using the library are provided under the "examples" directory.
|
||||
The src/bin/host was moved as part of the examples (and not
|
||||
installed with other BIND 10 programs any more).
|
||||
(Trac #1870, git 4973e638d354d8b56dcadf71123ef23c15662021)
|
||||
|
194
configure.ac
194
configure.ac
@ -70,6 +70,106 @@ AC_TRY_LINK([],[],
|
||||
])
|
||||
LDFLAGS=$LDFLAGS_SAVED
|
||||
|
||||
# Compiler dependent settings: define some mandatory CXXFLAGS here.
|
||||
# We also use a separate variable B10_CXXFLAGS. This will (and should) be
|
||||
# used as the default value for each specific AM_CXXFLAGS:
|
||||
# AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
# AM_CXXFLAGS += ... # add module specific flags
|
||||
# We need this so that we can disable some specific compiler warnings per
|
||||
# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since
|
||||
# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot
|
||||
# specify the default warning flags in CXXFLAGS and let specific modules
|
||||
# "override" the default.
|
||||
|
||||
# This may be used to try linker flags.
|
||||
AC_DEFUN([BIND10_CXX_TRY_FLAG], [
|
||||
AC_MSG_CHECKING([whether $CXX supports $1])
|
||||
|
||||
bind10_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $1"
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])],
|
||||
[bind10_cxx_flag=yes], [bind10_cxx_flag=no])
|
||||
CXXFLAGS="$bind10_save_CXXFLAGS"
|
||||
|
||||
if test "x$bind10_cxx_flag" = "xyes"; then
|
||||
ifelse([$2], , :, [$2])
|
||||
else
|
||||
ifelse([$3], , :, [$3])
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([$bind10_cxx_flag])
|
||||
])
|
||||
|
||||
# SunStudio compiler requires special compiler options for boost
|
||||
# (http://blogs.sun.com/sga/entry/boost_mini_howto)
|
||||
if test "$SUNCXX" = "yes"; then
|
||||
CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
|
||||
MULTITHREADING_FLAG="-mt"
|
||||
fi
|
||||
|
||||
# Newer versions of clang++ promotes "unused driver arguments" warnings to
|
||||
# a fatal error with -Werror, causing build failure. Since we use multiple
|
||||
# compilers on multiple systems, this can easily happen due to settings for
|
||||
# non clang++ environments that could be just ignored otherwise. It can also
|
||||
# happen if clang++ is used via ccache. So, although probably suboptimal,
|
||||
# we suppress this particular warning. Note that it doesn't weaken checks
|
||||
# on the source code.
|
||||
if test "$CLANGPP" = "yes"; then
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -Qunused-arguments"
|
||||
fi
|
||||
|
||||
BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers],
|
||||
[WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"])
|
||||
AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
|
||||
|
||||
# gcc specific settings:
|
||||
if test "X$GXX" = "Xyes"; then
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
|
||||
case "$host" in
|
||||
*-solaris*)
|
||||
MULTITHREADING_FLAG=-pthreads
|
||||
# In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces"
|
||||
;;
|
||||
*)
|
||||
MULTITHREADING_FLAG=-pthread
|
||||
;;
|
||||
esac
|
||||
|
||||
# Don't use -Werror if configured not to
|
||||
AC_ARG_WITH(werror,
|
||||
AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]),
|
||||
[
|
||||
case "${withval}" in
|
||||
yes) with_werror=1 ;;
|
||||
no) with_werror=0 ;;
|
||||
*) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;;
|
||||
esac],
|
||||
[with_werror=1])
|
||||
|
||||
werror_ok=0
|
||||
|
||||
# Certain versions of gcc (g++) have a bug that incorrectly warns about
|
||||
# the use of anonymous name spaces even if they're closed in a single
|
||||
# translation unit. For these versions we have to disable -Werror.
|
||||
if test $with_werror = 1; then
|
||||
CXXFLAGS_SAVED="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
|
||||
AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
|
||||
AC_TRY_COMPILE([namespace { class Foo {}; }
|
||||
namespace isc {class Bar {Foo foo_;};} ],,
|
||||
[AC_MSG_RESULT(no)
|
||||
werror_ok=1
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
|
||||
[AC_MSG_RESULT(yes)])
|
||||
CXXFLAGS="$CXXFLAGS_SAVED"
|
||||
fi
|
||||
|
||||
fi dnl GXX = yes
|
||||
|
||||
AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
|
||||
|
||||
# allow building programs with static link. we need to make it selective
|
||||
# because loadable modules cannot be statically linked.
|
||||
AC_ARG_ENABLE([static-link],
|
||||
@ -132,7 +232,7 @@ AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes)
|
||||
AC_SUBST(SET_ENV_LIBRARY_PATH)
|
||||
AC_SUBST(ENV_LIBRARY_PATH)
|
||||
|
||||
m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1 python3.2])
|
||||
m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3.2 python3.1 python3])
|
||||
AC_ARG_WITH([pythonpath],
|
||||
AC_HELP_STRING([--with-pythonpath=PATH],
|
||||
[specify an absolute path to python executable when automatic version check (incorrectly) fails]),
|
||||
@ -256,95 +356,11 @@ fi
|
||||
|
||||
# TODO: check for _sqlite3.py module
|
||||
|
||||
# Compiler dependent settings: define some mandatory CXXFLAGS here.
|
||||
# We also use a separate variable B10_CXXFLAGS. This will (and should) be
|
||||
# used as the default value for each specific AM_CXXFLAGS:
|
||||
# AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
# AM_CXXFLAGS += ... # add module specific flags
|
||||
# We need this so that we can disable some specific compiler warnings per
|
||||
# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since
|
||||
# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot
|
||||
# specify the default warning flags in CXXFLAGS and let specific modules
|
||||
# "override" the default.
|
||||
|
||||
# This may be used to try linker flags.
|
||||
AC_DEFUN([BIND10_CXX_TRY_FLAG], [
|
||||
AC_MSG_CHECKING([whether $CXX supports $1])
|
||||
|
||||
bind10_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $1"
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])],
|
||||
[bind10_cxx_flag=yes], [bind10_cxx_flag=no])
|
||||
CXXFLAGS="$bind10_save_CXXFLAGS"
|
||||
|
||||
if test "x$bind10_cxx_flag" = "xyes"; then
|
||||
ifelse([$2], , :, [$2])
|
||||
else
|
||||
ifelse([$3], , :, [$3])
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([$bind10_cxx_flag])
|
||||
])
|
||||
|
||||
# SunStudio compiler requires special compiler options for boost
|
||||
# (http://blogs.sun.com/sga/entry/boost_mini_howto)
|
||||
if test "$SUNCXX" = "yes"; then
|
||||
CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
|
||||
MULTITHREADING_FLAG="-mt"
|
||||
fi
|
||||
|
||||
BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers],
|
||||
[WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"])
|
||||
AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
|
||||
|
||||
# gcc specific settings:
|
||||
if test "X$GXX" = "Xyes"; then
|
||||
B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
|
||||
case "$host" in
|
||||
*-solaris*)
|
||||
MULTITHREADING_FLAG=-pthreads
|
||||
# In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces"
|
||||
;;
|
||||
*)
|
||||
MULTITHREADING_FLAG=-pthread
|
||||
;;
|
||||
esac
|
||||
|
||||
# Don't use -Werror if configured not to
|
||||
AC_ARG_WITH(werror,
|
||||
AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]),
|
||||
[
|
||||
case "${withval}" in
|
||||
yes) with_werror=1 ;;
|
||||
no) with_werror=0 ;;
|
||||
*) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;;
|
||||
esac],
|
||||
[with_werror=1])
|
||||
|
||||
werror_ok=0
|
||||
|
||||
# Certain versions of gcc (g++) have a bug that incorrectly warns about
|
||||
# the use of anonymous name spaces even if they're closed in a single
|
||||
# translation unit. For these versions we have to disable -Werror.
|
||||
if test $with_werror = 1; then
|
||||
CXXFLAGS_SAVED="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
|
||||
AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
|
||||
AC_TRY_COMPILE([namespace { class Foo {}; }
|
||||
namespace isc {class Bar {Foo foo_;};} ],,
|
||||
[AC_MSG_RESULT(no)
|
||||
werror_ok=1
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
|
||||
[AC_MSG_RESULT(yes)])
|
||||
CXXFLAGS="$CXXFLAGS_SAVED"
|
||||
fi
|
||||
|
||||
# (g++ only check)
|
||||
# Python 3.2 has an unused parameter in one of its headers. This
|
||||
# has been reported, but not fixed as of yet, so we check if we need
|
||||
# to set -Wno-unused-parameter.
|
||||
if test $werror_ok = 1; then
|
||||
if test "X$GXX" = "Xyes" -a $werror_ok = 1; then
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS=${PYTHON_INCLUDES}
|
||||
CXXFLAGS_SAVED="$CXXFLAGS"
|
||||
@ -370,10 +386,6 @@ if test $werror_ok = 1; then
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
fi
|
||||
|
||||
fi dnl GXX = yes
|
||||
|
||||
AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
|
||||
|
||||
# produce PIC unless we disable shared libraries. need this for python bindings.
|
||||
if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then
|
||||
B10_CXXFLAGS="$B10_CXXFLAGS -fPIC"
|
||||
|
@ -57,29 +57,6 @@
|
||||
* that does not support msgq. That is useful for embedded environments.
|
||||
* It may also be useful in validation.
|
||||
*
|
||||
* @page dhcpv6 DHCPv6 Server Component
|
||||
*
|
||||
* BIND10 offers DHCPv6 server implementation. It is implemented as
|
||||
* b10-dhcp6 component. Its primary code is located in
|
||||
* isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
|
||||
* especially lib::dhcp::Pkt6, isc::dhcp::Option and
|
||||
* isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
|
||||
* functionality, i.e. it is able to receive and process incoming
|
||||
* requests and trasmit responses. However, it does not have database
|
||||
* management, so it returns only one, hardcoded lease to whoever asks
|
||||
* for it.
|
||||
*
|
||||
* DHCPv6 server component does not support relayed traffic yet, as
|
||||
* support for relay decapsulation is not implemented yet.
|
||||
*
|
||||
* DHCPv6 server component does not use BIND10 logging yet.
|
||||
*
|
||||
* @section dhcpv6Session BIND10 message queue integration
|
||||
*
|
||||
* DHCPv4 server component is now integrated with BIND10 message queue.
|
||||
* It follows the same principle as DHCPv4. See \ref dhcpv4Session for
|
||||
* details.
|
||||
*
|
||||
* @page libdhcp libdhcp++
|
||||
*
|
||||
* @section libdhcpIntro Libdhcp++ Library Introduction
|
||||
|
@ -15,12 +15,17 @@
|
||||
* <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
|
||||
*
|
||||
* @section DNS
|
||||
* - Authoritative DNS (todo)
|
||||
* - Recursive resolver (todo)
|
||||
* - @subpage DataScrubbing
|
||||
*
|
||||
* @section DHCP
|
||||
* - @subpage dhcpv4
|
||||
* - @subpage dhcpv4Session
|
||||
* - @subpage dhcpv6
|
||||
* - @subpage dhcpv6-session
|
||||
* - @subpage dhcpv6-config-parser
|
||||
* - @subpage dhcpv6-config-inherit
|
||||
* - @subpage libdhcp
|
||||
* - @subpage libdhcpIntro
|
||||
* - @subpage libdhcpIfaceMgr
|
||||
|
@ -2751,13 +2751,13 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
|
||||
<title>DHCPv4 Server Configuration</title>
|
||||
<para>
|
||||
The DHCPv4 server does not have a lease database implemented yet
|
||||
nor any support for configuration, so every time the same set
|
||||
nor any support for configuration, so the same set
|
||||
of configuration options (including the same fixed address)
|
||||
will be assigned every time.
|
||||
</para>
|
||||
<para>
|
||||
At this stage of development, the only way to alter the server
|
||||
configuration is to tweak its source code. To do so, please
|
||||
configuration is to modify its source code. To do so, please
|
||||
edit src/bin/dhcp4/dhcp4_srv.cc file and modify following
|
||||
parameters and recompile:
|
||||
<screen>
|
||||
@ -2944,16 +2944,95 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
|
||||
<section id="dhcp6-config">
|
||||
<title>DHCPv6 Server Configuration</title>
|
||||
<para>
|
||||
The DHCPv6 server does not have lease database implemented yet
|
||||
or any support for configuration, so every time the same set
|
||||
of configuration options (including the same fixed address)
|
||||
will be assigned every time.
|
||||
Once the server is started, it can be configured. To view the
|
||||
current configuration, use the following command in <command>bindctl</command>:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp6</userinput></screen>
|
||||
When starting Dhcp6 daemon for the first time, the default configuration
|
||||
will be available. It will look similar to this:
|
||||
<screen>
|
||||
> <userinput>config show Dhcp6</userinput>
|
||||
Dhcp6/interface "eth0" string (default)
|
||||
Dhcp6/renew-timer 1000 integer (default)
|
||||
Dhcp6/rebind-timer 2000 integer (default)
|
||||
Dhcp6/preferred-lifetime 3000 integer (default)
|
||||
Dhcp6/valid-lifetime 4000 integer (default)
|
||||
Dhcp6/subnet6 [] list (default)</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To change one of the parameters, simply follow
|
||||
the usual <command>bindctl</command> procedure. For example, to make the
|
||||
leases longer, change their valid-lifetime parameter:
|
||||
<screen>
|
||||
> <userinput>config set Dhcp6/valid-lifetime 7200</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
Please note that most Dhcp6 parameters are of global scope
|
||||
and apply to all defined subnets, unless they are overridden on a
|
||||
per-subnet basis.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The essential role of DHCPv6 server is address assignment. The server
|
||||
has to be configured with at least one subnet and one pool of dynamic
|
||||
addresses to be managed. For example, assume that the server
|
||||
is connected to a network segment that uses the 2001:db8:1::/64
|
||||
prefix. The Administrator of that network has decided that addresses from range
|
||||
2001:db8:1::1 to 2001:db8:1::ffff are going to be managed by the Dhcp6
|
||||
server. Such a configuration can be achieved in the following way:
|
||||
<screen>
|
||||
> <userinput>config add Dhcp6/subnet6</userinput>
|
||||
> <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
|
||||
> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::0 - 2001:db8:1::ffff" ]</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
Note that subnet is defined as a simple string, but the pool parameter
|
||||
is actually a list of pools: for this reason, the pool definition is
|
||||
enclosed in square brackets, even though only one range of addresses
|
||||
is specified.</para>
|
||||
<para>It is possible to define more than one pool in a
|
||||
subnet: continuing the previous example, further assume that
|
||||
2001:db8:1:0:5::/80 should be also be managed by the server. It could be written as
|
||||
2001:db8:1:0:5:: to 2001:db8:1::5:ffff:ffff:ffff, but typing so many 'f's
|
||||
is cumbersome. It can be expressed more simply as 2001:db8:1:0:5::/80. Both
|
||||
formats are supported by Dhcp6 and can be mixed in the pool list.
|
||||
For example, one could define the following pools:
|
||||
<screen>
|
||||
> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ]</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
The number of pools is not limited, but for performance reasons it is recommended to
|
||||
use as few as possible.
|
||||
</para>
|
||||
<para>
|
||||
At this stage of development, the only way to alter server
|
||||
configuration is to tweak its source code. To do so, please
|
||||
edit src/bin/dhcp6/dhcp6_srv.cc file, modify the following
|
||||
parameters and recompile:
|
||||
The server may be configured to serve more than one subnet. To add a second subnet,
|
||||
use a command similar to the following:
|
||||
<screen>
|
||||
> <userinput>config add Dhcp6/subnet6</userinput>
|
||||
> <userinput>config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48"</userinput>
|
||||
> <userinput>config set Dhcp6/subnet6[1]/pool [ "2001:db8:beef::/48" ]</userinput>
|
||||
> <userinput>config commit</userinput></screen>
|
||||
Arrays are counted from 0. subnet[0] refers to the subnet defined in the
|
||||
previous example. The <command>config add Dhcp6/subnet6</command> adds
|
||||
another (second) subnet. It can be referred to as
|
||||
<command>Dhcp6/subnet6[1]</command>. In this example, we allow server to
|
||||
dynamically assign all addresses available in the whole subnet. Although
|
||||
very wasteful, it is certainly a valid configuration to dedicate the
|
||||
whole /48 subnet for that purpose.
|
||||
</para>
|
||||
<para>
|
||||
When configuring a DHCPv6 server using prefix/length notation, please pay
|
||||
attention to the boundary values. When specifying that the server should use
|
||||
a given pool, it will be able to allocate also first (typically network
|
||||
address) address from that pool. For example for pool 2001:db8::/64 the
|
||||
2001:db8:: address may be assigned as well. If you want to avoid this,
|
||||
please use min-max notation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note: Although configuration is now accepted, it is not internally used
|
||||
by they server yet. At this stage of development, the only way to alter
|
||||
server configuration is to modify its source code. To do so, please edit
|
||||
src/bin/dhcp6/dhcp6_srv.cc file, modify the following parameters and
|
||||
recompile:
|
||||
<screen>
|
||||
const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
|
||||
const uint32_t HARDCODED_T1 = 1500; // in seconds
|
||||
|
@ -7,7 +7,7 @@ this directory.
|
||||
|
||||
On the top (sub) directory (where this README file is stored), we
|
||||
provide a sample configure.ac and Makefile.am files for GNU automake
|
||||
environments with helper autoconf macros to detect the available and
|
||||
environments with helper autoconf macros to detect the availability and
|
||||
location of BIND 10 header files and library objects.
|
||||
|
||||
You can use the configure.ac and Makefile.am files with macros under
|
||||
|
@ -93,7 +93,7 @@ public:
|
||||
/// that corresponds to this derived class and prepares a new value to
|
||||
/// apply to the server.
|
||||
/// In the above example, the derived class for the identifier "param1"
|
||||
/// would be passed an data \c Element storing an integer whose value
|
||||
/// would be passed a data \c Element storing an integer whose value
|
||||
/// is 10, and would record that value internally;
|
||||
/// the derived class for the identifier "param2" would be passed a
|
||||
/// map element and (after parsing) convert it into some internal
|
||||
|
@ -69,6 +69,8 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::cc;
|
||||
using namespace isc::datasrc;
|
||||
@ -264,23 +266,22 @@ public:
|
||||
AddressList listen_addresses_;
|
||||
|
||||
/// The TSIG keyring
|
||||
const boost::shared_ptr<TSIGKeyRing>* keyring_;
|
||||
const shared_ptr<TSIGKeyRing>* keyring_;
|
||||
|
||||
/// The client list
|
||||
std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >
|
||||
client_lists_;
|
||||
/// The data source client list
|
||||
AuthSrv::DataSrcClientListsPtr datasrc_client_lists_;
|
||||
|
||||
boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
|
||||
rrclass)
|
||||
shared_ptr<ConfigurableClientList> getDataSrcClientList(
|
||||
const RRClass& rrclass)
|
||||
{
|
||||
// TODO: Debug-build only check
|
||||
if (!mutex_.locked()) {
|
||||
isc_throw(isc::Unexpected, "Not locked!");
|
||||
}
|
||||
const std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
|
||||
const_iterator it(client_lists_.find(rrclass));
|
||||
if (it == client_lists_.end()) {
|
||||
return (boost::shared_ptr<ConfigurableClientList>());
|
||||
const std::map<RRClass, shared_ptr<ConfigurableClientList> >::
|
||||
const_iterator it(datasrc_client_lists_->find(rrclass));
|
||||
if (it == datasrc_client_lists_->end()) {
|
||||
return (shared_ptr<ConfigurableClientList>());
|
||||
} else {
|
||||
return (it->second);
|
||||
}
|
||||
@ -335,6 +336,8 @@ AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
|
||||
xfrin_session_(NULL),
|
||||
counters_(),
|
||||
keyring_(NULL),
|
||||
datasrc_client_lists_(new std::map<RRClass,
|
||||
shared_ptr<ConfigurableClientList> >()),
|
||||
ddns_base_forwarder_(ddns_forwarder),
|
||||
ddns_forwarder_(NULL),
|
||||
xfrout_connected_(false),
|
||||
@ -645,13 +648,13 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
|
||||
}
|
||||
// Lock the client lists and keep them under the lock until the processing
|
||||
// and rendering is done (this is the same mutex as from
|
||||
// AuthSrv::getClientListMutex()).
|
||||
// AuthSrv::getDataSrcClientListMutex()).
|
||||
isc::util::thread::Mutex::Locker locker(mutex_);
|
||||
|
||||
try {
|
||||
const ConstQuestionPtr question = *message.beginQuestion();
|
||||
const boost::shared_ptr<datasrc::ClientList>
|
||||
list(getClientList(question->getClass()));
|
||||
const shared_ptr<datasrc::ClientList>
|
||||
list(getDataSrcClientList(question->getClass()));
|
||||
if (list) {
|
||||
const RRType& qtype = question->getType();
|
||||
const Name& qname = question->getName();
|
||||
@ -911,7 +914,7 @@ AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
|
||||
}
|
||||
|
||||
void
|
||||
AuthSrv::setTSIGKeyRing(const boost::shared_ptr<TSIGKeyRing>* keyring) {
|
||||
AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
|
||||
impl_->keyring_ = keyring;
|
||||
}
|
||||
|
||||
@ -930,43 +933,23 @@ AuthSrv::destroyDDNSForwarder() {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AuthSrv::setClientList(const RRClass& rrclass,
|
||||
const boost::shared_ptr<ConfigurableClientList>& list) {
|
||||
AuthSrv::DataSrcClientListsPtr
|
||||
AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) {
|
||||
// TODO: Debug-build only check
|
||||
if (!impl_->mutex_.locked()) {
|
||||
isc_throw(isc::Unexpected, "Not locked");
|
||||
isc_throw(isc::Unexpected, "Not locked!");
|
||||
}
|
||||
|
||||
if (list) {
|
||||
impl_->client_lists_[rrclass] = list;
|
||||
} else {
|
||||
impl_->client_lists_.erase(rrclass);
|
||||
}
|
||||
}
|
||||
boost::shared_ptr<ConfigurableClientList>
|
||||
AuthSrv::getClientList(const RRClass& rrclass) {
|
||||
return (impl_->getClientList(rrclass));
|
||||
std::swap(new_lists, impl_->datasrc_client_lists_);
|
||||
return (new_lists);
|
||||
}
|
||||
|
||||
vector<RRClass>
|
||||
AuthSrv::getClientListClasses() const {
|
||||
// TODO: Debug-build only check
|
||||
if (!impl_->mutex_.locked()) {
|
||||
isc_throw(isc::Unexpected, "Not locked");
|
||||
}
|
||||
|
||||
vector<RRClass> result;
|
||||
for (std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
|
||||
const_iterator it(impl_->client_lists_.begin());
|
||||
it != impl_->client_lists_.end(); ++it) {
|
||||
result.push_back(it->first);
|
||||
}
|
||||
return (result);
|
||||
shared_ptr<ConfigurableClientList>
|
||||
AuthSrv::getDataSrcClientList(const RRClass& rrclass) {
|
||||
return (impl_->getDataSrcClientList(rrclass));
|
||||
}
|
||||
|
||||
util::thread::Mutex&
|
||||
AuthSrv::getClientListMutex() const {
|
||||
AuthSrv::getDataSrcClientListMutex() const {
|
||||
return (impl_->mutex_);
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,11 @@
|
||||
#ifndef __AUTH_SRV_H
|
||||
#define __AUTH_SRV_H 1
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <config/ccsession.h>
|
||||
|
||||
#include <datasrc/factory.h>
|
||||
#include <datasrc/client_list.h>
|
||||
|
||||
#include <dns/message.h>
|
||||
#include <dns/opcode.h>
|
||||
#include <util/buffer.h>
|
||||
@ -35,6 +36,11 @@
|
||||
#include <server_common/portconfig.h>
|
||||
#include <auth/statistics.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace isc {
|
||||
namespace util {
|
||||
namespace io {
|
||||
@ -296,31 +302,46 @@ public:
|
||||
/// If there was no forwarder yet, this method does nothing.
|
||||
void destroyDDNSForwarder();
|
||||
|
||||
/// \brief Sets the currently used list for data sources of given
|
||||
/// class.
|
||||
/// \brief Shortcut typedef used for swapDataSrcClientLists().
|
||||
typedef boost::shared_ptr<std::map<
|
||||
isc::dns::RRClass, boost::shared_ptr<
|
||||
isc::datasrc::ConfigurableClientList> > >
|
||||
DataSrcClientListsPtr;
|
||||
|
||||
/// \brief Swap the currently used set of data source client lists with
|
||||
/// given one.
|
||||
///
|
||||
/// Replaces the internally used client list with a new one. Other
|
||||
/// classes are not changed.
|
||||
/// The "set" of lists is actually given in the form of map from
|
||||
/// RRClasses to shared pointers to isc::datasrc::ConfigurableClientList.
|
||||
///
|
||||
/// \param rrclass The class to modify.
|
||||
/// \param list Shared pointer to the client list. If it is NULL,
|
||||
/// the list is removed instead.
|
||||
void setClientList(const isc::dns::RRClass& rrclass, const
|
||||
boost::shared_ptr<isc::datasrc::ConfigurableClientList>&
|
||||
list);
|
||||
/// This method returns the swapped set of lists, which was previously
|
||||
/// used by the server.
|
||||
///
|
||||
/// This method is intended to be used by a separate method to update
|
||||
/// the data source configuration "at once". The caller must hold
|
||||
/// a lock for the mutex object returned by \c getDataSrcClientListMutex()
|
||||
/// before calling this method.
|
||||
///
|
||||
/// The ownership of the returned pointer is transferred to the caller.
|
||||
/// The caller is generally expected to release the resources used in
|
||||
/// the old lists. Note that it could take longer time if some of the
|
||||
/// data source clients contain a large size of in-memory data.
|
||||
///
|
||||
/// The caller can pass a NULL pointer. This effectively disables
|
||||
/// any data source for the server.
|
||||
///
|
||||
/// \param new_lists Shared pointer to a new set of data source client
|
||||
/// lists.
|
||||
/// \return The previous set of lists. It can be NULL.
|
||||
DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr
|
||||
new_lists);
|
||||
|
||||
/// \brief Returns the currently used client list for the class.
|
||||
///
|
||||
/// \param rrclass The class for which to get the list.
|
||||
/// \return The list, or NULL if no list is set for the class.
|
||||
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
|
||||
getClientList(const isc::dns::RRClass& rrclass);
|
||||
|
||||
/// \brief Returns a list of classes that have a client list.
|
||||
///
|
||||
/// \return List of classes for which a non-NULL client list
|
||||
/// has been set by setClientList.
|
||||
std::vector<isc::dns::RRClass> getClientListClasses() const;
|
||||
getDataSrcClientList(const isc::dns::RRClass& rrclass);
|
||||
|
||||
/// \brief Return a mutex for the client lists.
|
||||
///
|
||||
@ -331,9 +352,9 @@ public:
|
||||
/// is correct:
|
||||
/// \code
|
||||
/// {
|
||||
/// Mutex::Locker locker(auth->getClientListMutex());
|
||||
/// Mutex::Locker locker(auth->getDataSrcClientListMutex());
|
||||
/// boost::shared_ptr<isc::datasrc::ConfigurableClientList>
|
||||
/// list(auth->getClientList(RRClass::IN()));
|
||||
/// list(auth->getDataSrcClientList(RRClass::IN()));
|
||||
/// // Do some processing here
|
||||
/// }
|
||||
/// \endcode
|
||||
@ -342,8 +363,8 @@ public:
|
||||
/// \code
|
||||
/// boost::shared_ptr<isc::datasrc::ConfigurableClientList> list;
|
||||
/// {
|
||||
/// Mutex::Locker locker(auth->getClientListMutex());
|
||||
/// list = auth->getClientList(RRClass::IN()));
|
||||
/// Mutex::Locker locker(auth->getDataSrcClientListMutex());
|
||||
/// list = auth->getDataSrcClientList(RRClass::IN()));
|
||||
/// }
|
||||
/// // Do some processing here
|
||||
/// \endcode
|
||||
@ -352,7 +373,7 @@ public:
|
||||
/// (lock) the mutex. It's because locking of the mutex is not really
|
||||
/// a modification of the server object and it is needed to protect the
|
||||
/// lists even on read-only operations.
|
||||
isc::util::thread::Mutex& getClientListMutex() const;
|
||||
isc::util::thread::Mutex& getDataSrcClientListMutex() const;
|
||||
|
||||
/// \brief Sets the timeout for incoming TCP connections
|
||||
///
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <bench/benchmark_util.h>
|
||||
|
||||
#include <util/buffer.h>
|
||||
#include <util/threads/lock.h>
|
||||
|
||||
#include <dns/message.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/question.h>
|
||||
@ -125,13 +127,15 @@ public:
|
||||
OutputBuffer& buffer) :
|
||||
QueryBenchMark(queries, query_message, buffer)
|
||||
{
|
||||
configureDataSource(
|
||||
*server_,
|
||||
Element::fromJSON("{\"IN\":"
|
||||
" [{\"type\": \"sqlite3\","
|
||||
" \"params\": {"
|
||||
" \"database_file\": \"" +
|
||||
string(datasrc_file) + "\"}}]}"));
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server_->getDataSrcClientListMutex());
|
||||
server_->swapDataSrcClientLists(
|
||||
configureDataSource(
|
||||
Element::fromJSON("{\"IN\":"
|
||||
" [{\"type\": \"sqlite3\","
|
||||
" \"params\": {"
|
||||
" \"database_file\": \"" +
|
||||
string(datasrc_file) + "\"}}]}")));
|
||||
}
|
||||
};
|
||||
|
||||
@ -144,14 +148,16 @@ public:
|
||||
OutputBuffer& buffer) :
|
||||
QueryBenchMark(queries, query_message, buffer)
|
||||
{
|
||||
configureDataSource(
|
||||
*server_,
|
||||
Element::fromJSON("{\"IN\":"
|
||||
" [{\"type\": \"MasterFiles\","
|
||||
" \"cache-enable\": true, "
|
||||
" \"params\": {\"" +
|
||||
string(zone_origin) + "\": \"" +
|
||||
string(zone_file) + "\"}}]}"));
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server_->getDataSrcClientListMutex());
|
||||
server_->swapDataSrcClientLists(
|
||||
configureDataSource(
|
||||
Element::fromJSON("{\"IN\":"
|
||||
" [{\"type\": \"MasterFiles\","
|
||||
" \"cache-enable\": true, "
|
||||
" \"params\": {\"" +
|
||||
string(zone_origin) + "\": \"" +
|
||||
string(zone_file) + "\"}}]}")));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -192,9 +192,10 @@ public:
|
||||
|
||||
// We're going to work with the client lists. They may be used
|
||||
// from a different thread too, protect them.
|
||||
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server.getDataSrcClientListMutex());
|
||||
const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
|
||||
list(server.getClientList(zone_class));
|
||||
list(server.getDataSrcClientList(zone_class));
|
||||
|
||||
if (!list) {
|
||||
isc_throw(AuthCommandError, "There's no client list for "
|
||||
|
@ -18,9 +18,8 @@
|
||||
|
||||
// This is a trivial specialization for the commonly used version.
|
||||
// Defined in .cc to avoid accidental creation of multiple copies.
|
||||
void
|
||||
configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config)
|
||||
{
|
||||
return (configureDataSourceGeneric<AuthSrv,
|
||||
isc::datasrc::ConfigurableClientList>(server, config));
|
||||
AuthSrv::DataSrcClientListsPtr
|
||||
configureDataSource(const isc::data::ConstElementPtr& config) {
|
||||
return (configureDataSourceGeneric<
|
||||
isc::datasrc::ConfigurableClientList>(config));
|
||||
}
|
||||
|
@ -19,99 +19,62 @@
|
||||
|
||||
#include <cc/data.h>
|
||||
#include <datasrc/client_list.h>
|
||||
#include <util/threads/lock.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <utility>
|
||||
#include <set>
|
||||
|
||||
/// \brief Configure the authoritative server's data source lists
|
||||
/// \brief Configure data source client lists
|
||||
///
|
||||
/// This will hook into the data_sources module configuration and it will
|
||||
/// keep the local copy of data source clients in the list in the authoritative
|
||||
/// server.
|
||||
/// return a new set (in the form of a shared pointer to map) of data source
|
||||
/// client lists corresponding to the configuration.
|
||||
///
|
||||
/// This function is templated. This is simply because of easier testing.
|
||||
/// You don't need to pay attention to it, use the configureDataSource
|
||||
/// specialization instead.
|
||||
///
|
||||
/// \param server It is the server to configure.
|
||||
/// \note In future we may want to make the reconfiguration more efficient
|
||||
/// by only creating newly configured data and just moving the rest from
|
||||
/// the running configuration if they are used in the new configuration
|
||||
/// without any parameter change. We could probably do it by passing
|
||||
/// the old lists in addition to the new config, but further details are
|
||||
/// still to be defined yet. It will surely require changes in the
|
||||
/// data source library, too. So, right now, we don't introduce the
|
||||
/// possibility in the function interface. If and when we decide to introduce
|
||||
/// the optimization, we'll extend the interface.
|
||||
///
|
||||
/// \param config The configuration value to parse. It is in the form
|
||||
/// as an update from the config manager.
|
||||
template<class Server, class List>
|
||||
void
|
||||
configureDataSourceGeneric(Server& server,
|
||||
const isc::data::ConstElementPtr& config)
|
||||
{
|
||||
/// \return A map from RR classes to configured lists.
|
||||
template<class List>
|
||||
boost::shared_ptr<std::map<isc::dns::RRClass,
|
||||
boost::shared_ptr<List> > > // = ListMap below
|
||||
configureDataSourceGeneric(const isc::data::ConstElementPtr& config) {
|
||||
typedef boost::shared_ptr<List> ListPtr;
|
||||
typedef std::map<std::string, isc::data::ConstElementPtr> Map;
|
||||
typedef std::pair<isc::dns::RRClass, ListPtr> RollbackPair;
|
||||
typedef std::pair<isc::dns::RRClass, isc::data::ConstElementPtr>
|
||||
RollbackConfiguration;
|
||||
typedef std::map<isc::dns::RRClass, ListPtr> ListMap;
|
||||
|
||||
// Lock the client lists, we're going to manipulate them.
|
||||
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
|
||||
boost::shared_ptr<ListMap> new_lists(new ListMap);
|
||||
|
||||
// Some structures to be able to perform a rollback
|
||||
std::vector<RollbackPair> rollback_sets;
|
||||
std::vector<RollbackConfiguration> rollback_configurations;
|
||||
try {
|
||||
// Get the configuration and current state.
|
||||
const Map& map(config->mapValue());
|
||||
const std::vector<isc::dns::RRClass>
|
||||
activeVector(server.getClientListClasses());
|
||||
std::set<isc::dns::RRClass> active(activeVector.begin(),
|
||||
activeVector.end());
|
||||
// Go through the configuration and change everything.
|
||||
for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
|
||||
const isc::dns::RRClass rrclass(it->first);
|
||||
active.erase(rrclass);
|
||||
ListPtr list(server.getClientList(rrclass));
|
||||
bool need_set(false);
|
||||
if (list) {
|
||||
rollback_configurations.
|
||||
push_back(RollbackConfiguration(rrclass,
|
||||
list->getConfiguration()));
|
||||
} else {
|
||||
list.reset(new List(rrclass));
|
||||
need_set = true;
|
||||
rollback_sets.push_back(RollbackPair(rrclass, ListPtr()));
|
||||
}
|
||||
list->configure(it->second, true);
|
||||
if (need_set) {
|
||||
server.setClientList(rrclass, list);
|
||||
}
|
||||
}
|
||||
// Remove the ones that are not in the configuration.
|
||||
for (std::set<isc::dns::RRClass>::iterator it(active.begin());
|
||||
it != active.end(); ++it) {
|
||||
// There seems to be no way the setClientList could throw.
|
||||
// But this is just to make sure in case it did to restore
|
||||
// the original.
|
||||
rollback_sets.push_back(
|
||||
RollbackPair(*it, server.getClientList(*it)));
|
||||
server.setClientList(*it, ListPtr());
|
||||
}
|
||||
} catch (...) {
|
||||
// Perform a rollback of the changes. The old configuration should
|
||||
// work.
|
||||
for (typename std::vector<RollbackPair>::const_iterator
|
||||
it(rollback_sets.begin()); it != rollback_sets.end(); ++it) {
|
||||
server.setClientList(it->first, it->second);
|
||||
}
|
||||
for (typename std::vector<RollbackConfiguration>::const_iterator
|
||||
it(rollback_configurations.begin());
|
||||
it != rollback_configurations.end(); ++it) {
|
||||
server.getClientList(it->first)->configure(it->second, true);
|
||||
}
|
||||
throw;
|
||||
// Go through the configuration and create corresponding list.
|
||||
const Map& map(config->mapValue());
|
||||
for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
|
||||
const isc::dns::RRClass rrclass(it->first);
|
||||
ListPtr list(new List(rrclass));
|
||||
list->configure(it->second, true);
|
||||
new_lists->insert(std::pair<isc::dns::RRClass, ListPtr>(rrclass,
|
||||
list));
|
||||
}
|
||||
|
||||
return (new_lists);
|
||||
}
|
||||
|
||||
/// \brief Concrete version of configureDataSource() for the
|
||||
/// use with authoritative server implementation.
|
||||
void
|
||||
configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config);
|
||||
AuthSrv::DataSrcClientListsPtr
|
||||
configureDataSource(const isc::data::ConstElementPtr& config);
|
||||
|
||||
#endif // DATASRC_CONFIG_H
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <util/buffer.h>
|
||||
#include <util/io/socketsession.h>
|
||||
#include <util/threads/lock.h>
|
||||
|
||||
#include <dns/message.h>
|
||||
#include <dns/messagerenderer.h>
|
||||
@ -93,18 +94,31 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time,
|
||||
{
|
||||
assert(server != NULL);
|
||||
if (config->contains("classes")) {
|
||||
AuthSrv::DataSrcClientListsPtr lists;
|
||||
|
||||
if (*first_time) {
|
||||
// HACK: The default is not passed to the handler in the first
|
||||
// callback. This one will get the default (or, current value).
|
||||
// Further updates will work the usual way.
|
||||
assert(config_session != NULL);
|
||||
*first_time = false;
|
||||
configureDataSource(*auth_server,
|
||||
config_session->getRemoteConfigValue(
|
||||
"data_sources", "classes"));
|
||||
lists = configureDataSource(
|
||||
config_session->getRemoteConfigValue("data_sources",
|
||||
"classes"));
|
||||
} else {
|
||||
configureDataSource(*server, config->get("classes"));
|
||||
lists = configureDataSource(config->get("classes"));
|
||||
}
|
||||
|
||||
// Replace the server's lists. The returned lists will be stored
|
||||
// in a local variable 'lists', and will be destroyed outside of
|
||||
// the temporary block for the lock scope. That way we can minimize
|
||||
// the range of the critical section.
|
||||
{
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server->getDataSrcClientListMutex());
|
||||
lists = server->swapDataSrcClientLists(lists);
|
||||
}
|
||||
// The previous lists are destroyed here.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,7 @@
|
||||
using namespace std;
|
||||
using namespace isc::cc;
|
||||
using namespace isc::dns;
|
||||
using namespace isc::datasrc;
|
||||
using namespace isc::util;
|
||||
using namespace isc::util::io::internal;
|
||||
using namespace isc::util::unittests;
|
||||
@ -90,6 +91,9 @@ const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone";
|
||||
// a signed example zone.
|
||||
const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed";
|
||||
|
||||
// shortcut commonly used in tests
|
||||
typedef boost::shared_ptr<ConfigurableClientList> ListPtr;
|
||||
|
||||
class AuthSrvTest : public SrvTestBase {
|
||||
protected:
|
||||
AuthSrvTest() :
|
||||
@ -721,6 +725,14 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
|
||||
EXPECT_FALSE(dnsserv.hasAnswer());
|
||||
}
|
||||
|
||||
void
|
||||
installDataSrcClientLists(AuthSrv& server,
|
||||
AuthSrv::DataSrcClientListsPtr lists)
|
||||
{
|
||||
thread::Mutex::Locker locker(server.getDataSrcClientListMutex());
|
||||
server.swapDataSrcClientLists(lists);
|
||||
}
|
||||
|
||||
void
|
||||
updateDatabase(AuthSrv& server, const char* params) {
|
||||
const ConstElementPtr config(Element::fromJSON("{"
|
||||
@ -728,7 +740,7 @@ updateDatabase(AuthSrv& server, const char* params) {
|
||||
" \"type\": \"sqlite3\","
|
||||
" \"params\": " + string(params) +
|
||||
"}]}"));
|
||||
configureDataSource(server, config);
|
||||
installDataSrcClientLists(server, configureDataSource(config));
|
||||
}
|
||||
|
||||
void
|
||||
@ -745,7 +757,7 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename) {
|
||||
" \"type\": \"static\","
|
||||
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
|
||||
"}]}"));
|
||||
configureDataSource(server, config);
|
||||
installDataSrcClientLists(server, configureDataSource(config));
|
||||
}
|
||||
|
||||
void
|
||||
@ -755,7 +767,7 @@ updateBuiltin(AuthSrv& server) {
|
||||
" \"type\": \"static\","
|
||||
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
|
||||
"}]}"));
|
||||
configureDataSource(server, config);
|
||||
installDataSrcClientLists(server, configureDataSource(config));
|
||||
}
|
||||
|
||||
// Try giving the server a TSIG signed request and see it can anwer signed as
|
||||
@ -953,7 +965,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
|
||||
" \"params\": {},"
|
||||
" \"cache-enable\": true"
|
||||
"}]}"));
|
||||
configureDataSource(server, config);
|
||||
installDataSrcClientLists(server, configureDataSource(config));
|
||||
// after successful configuration, we should have one (with empty zoneset).
|
||||
|
||||
// The memory data source is empty, should return REFUSED rcode.
|
||||
@ -1427,11 +1439,14 @@ TEST_F(AuthSrvTest,
|
||||
// Set real inmem client to proxy
|
||||
updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
|
||||
{
|
||||
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server.getDataSrcClientListMutex());
|
||||
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
|
||||
list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER,
|
||||
false));
|
||||
server.setClientList(RRClass::IN(), list);
|
||||
list(new FakeList(server.getDataSrcClientList(RRClass::IN()),
|
||||
THROW_NEVER, false));
|
||||
AuthSrv::DataSrcClientListsPtr lists(new std::map<RRClass, ListPtr>);
|
||||
lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
|
||||
server.swapDataSrcClientLists(lists);
|
||||
}
|
||||
|
||||
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
|
||||
@ -1455,11 +1470,14 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception,
|
||||
{
|
||||
updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
|
||||
|
||||
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server.getDataSrcClientListMutex());
|
||||
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
|
||||
list(new FakeList(server.getClientList(RRClass::IN()), throw_when,
|
||||
isc_exception, rrset));
|
||||
server.setClientList(RRClass::IN(), list);
|
||||
list(new FakeList(server.getDataSrcClientList(RRClass::IN()),
|
||||
throw_when, isc_exception, rrset));
|
||||
AuthSrv::DataSrcClientListsPtr lists(new std::map<RRClass, ListPtr>);
|
||||
lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
|
||||
server.swapDataSrcClientLists(lists);
|
||||
}
|
||||
|
||||
TEST_F(AuthSrvTest,
|
||||
@ -1771,46 +1789,51 @@ TEST_F(AuthSrvTest, clientList) {
|
||||
// We need to lock the mutex to make the (get|set)ClientList happy.
|
||||
// There's a debug-build only check in them to make sure everything
|
||||
// locks them and we call them directly here.
|
||||
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server.getDataSrcClientListMutex());
|
||||
|
||||
AuthSrv::DataSrcClientListsPtr lists; // initially empty
|
||||
|
||||
// The lists don't exist. Therefore, the list of RRClasses is empty.
|
||||
// We also have no IN list.
|
||||
EXPECT_TRUE(server.getClientListClasses().empty());
|
||||
EXPECT_EQ(boost::shared_ptr<const isc::datasrc::ClientList>(),
|
||||
server.getClientList(RRClass::IN()));
|
||||
EXPECT_TRUE(server.swapDataSrcClientLists(lists)->empty());
|
||||
|
||||
// Put something in.
|
||||
const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
|
||||
list(new isc::datasrc::ConfigurableClientList(RRClass::IN()));
|
||||
const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
|
||||
list2(new isc::datasrc::ConfigurableClientList(RRClass::CH()));
|
||||
server.setClientList(RRClass::IN(), list);
|
||||
server.setClientList(RRClass::CH(), list2);
|
||||
// There are two things in the list and they are IN and CH
|
||||
vector<RRClass> classes(server.getClientListClasses());
|
||||
ASSERT_EQ(2, classes.size());
|
||||
EXPECT_EQ(RRClass::IN(), classes[0]);
|
||||
EXPECT_EQ(RRClass::CH(), classes[1]);
|
||||
const ListPtr list(new ConfigurableClientList(RRClass::IN()));
|
||||
const ListPtr list2(new ConfigurableClientList(RRClass::CH()));
|
||||
|
||||
lists.reset(new std::map<RRClass, ListPtr>);
|
||||
lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
|
||||
lists->insert(pair<RRClass, ListPtr>(RRClass::CH(), list2));
|
||||
server.swapDataSrcClientLists(lists);
|
||||
|
||||
// And the lists can be retrieved.
|
||||
EXPECT_EQ(list, server.getClientList(RRClass::IN()));
|
||||
EXPECT_EQ(list2, server.getClientList(RRClass::CH()));
|
||||
// Remove one of them
|
||||
server.setClientList(RRClass::CH(),
|
||||
boost::shared_ptr<isc::datasrc::ConfigurableClientList>());
|
||||
// This really got deleted, including the class.
|
||||
classes = server.getClientListClasses();
|
||||
ASSERT_EQ(1, classes.size());
|
||||
EXPECT_EQ(RRClass::IN(), classes[0]);
|
||||
EXPECT_EQ(list, server.getClientList(RRClass::IN()));
|
||||
EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN()));
|
||||
EXPECT_EQ(list2, server.getDataSrcClientList(RRClass::CH()));
|
||||
|
||||
// Replace the lists with new lists containing only one list.
|
||||
lists.reset(new std::map<RRClass, ListPtr>);
|
||||
lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
|
||||
lists = server.swapDataSrcClientLists(lists);
|
||||
|
||||
// Old one had two lists. That confirms our swap for IN and CH classes
|
||||
// (i.e., no other entries were there).
|
||||
EXPECT_EQ(2, lists->size());
|
||||
|
||||
// The CH list really got deleted.
|
||||
EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN()));
|
||||
EXPECT_FALSE(server.getDataSrcClientList(RRClass::CH()));
|
||||
}
|
||||
|
||||
// We just test the mutex can be locked (exactly once).
|
||||
TEST_F(AuthSrvTest, mutex) {
|
||||
isc::util::thread::Mutex::Locker l1(server.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker l1(server.getDataSrcClientListMutex());
|
||||
// TODO: Once we have non-debug build, this one will not work, since
|
||||
// we currently use the fact that we can't lock twice from the same
|
||||
// thread. In the non-debug mode, this would deadlock.
|
||||
// Skip then.
|
||||
EXPECT_THROW({
|
||||
isc::util::thread::Mutex::Locker l2(server.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker l2(
|
||||
server.getDataSrcClientListMutex());
|
||||
}, isc::InvalidOperation);
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
#include "datasrc_util.h"
|
||||
|
||||
#include <util/threads/lock.h>
|
||||
|
||||
#include <auth/auth_srv.h>
|
||||
#include <auth/auth_config.h>
|
||||
#include <auth/command.h>
|
||||
@ -174,21 +176,31 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) {
|
||||
// zones, and checks the zones are correctly loaded.
|
||||
void
|
||||
zoneChecks(AuthSrv& server) {
|
||||
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server.getDataSrcClientListMutex());
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test1.example")).finder_->
|
||||
find(Name("ns.test1.example"), RRType::A())->code);
|
||||
EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test1.example")).finder_->
|
||||
find(Name("ns.test1.example"), RRType::AAAA())->code);
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test2.example")).finder_->
|
||||
find(Name("ns.test2.example"), RRType::A())->code);
|
||||
EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test2.example")).finder_->
|
||||
find(Name("ns.test2.example"), RRType::AAAA())->code);
|
||||
}
|
||||
|
||||
void
|
||||
installDataSrcClientLists(AuthSrv& server,
|
||||
AuthSrv::DataSrcClientListsPtr lists)
|
||||
{
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server.getDataSrcClientListMutex());
|
||||
server.swapDataSrcClientLists(lists);
|
||||
}
|
||||
|
||||
void
|
||||
configureZones(AuthSrv& server) {
|
||||
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
|
||||
@ -208,27 +220,29 @@ configureZones(AuthSrv& server) {
|
||||
" \"cache-enable\": true"
|
||||
"}]}"));
|
||||
|
||||
configureDataSource(server, config);
|
||||
installDataSrcClientLists(server, configureDataSource(config));
|
||||
|
||||
zoneChecks(server);
|
||||
}
|
||||
|
||||
void
|
||||
newZoneChecks(AuthSrv& server) {
|
||||
isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server.getDataSrcClientListMutex());
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test1.example")).finder_->
|
||||
find(Name("ns.test1.example"), RRType::A())->code);
|
||||
// now test1.example should have ns/AAAA
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test1.example")).finder_->
|
||||
find(Name("ns.test1.example"), RRType::AAAA())->code);
|
||||
|
||||
// test2.example shouldn't change
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test2.example")).finder_->
|
||||
find(Name("ns.test2.example"), RRType::A())->code);
|
||||
EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::NXRRSET,
|
||||
server.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("ns.test2.example")).finder_->
|
||||
find(Name("ns.test2.example"), RRType::AAAA())->code);
|
||||
}
|
||||
@ -271,12 +285,14 @@ TEST_F(AuthCommandTest,
|
||||
" \"cache-enable\": true,"
|
||||
" \"cache-zones\": [\"example.org\"]"
|
||||
"}]}"));
|
||||
configureDataSource(server_, config);
|
||||
installDataSrcClientLists(server_, configureDataSource(config));
|
||||
|
||||
{
|
||||
isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server_.getDataSrcClientListMutex());
|
||||
// Check that the A record at www.example.org does not exist
|
||||
EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::NXDOMAIN,
|
||||
server_.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("example.org")).finder_->
|
||||
find(Name("www.example.org"), RRType::A())->code);
|
||||
|
||||
@ -296,7 +312,8 @@ TEST_F(AuthCommandTest,
|
||||
sql_updater->addRRset(*rrset);
|
||||
sql_updater->commit();
|
||||
|
||||
EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::NXDOMAIN,
|
||||
server_.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("example.org")).finder_->
|
||||
find(Name("www.example.org"), RRType::A())->code);
|
||||
}
|
||||
@ -308,9 +325,11 @@ TEST_F(AuthCommandTest,
|
||||
checkAnswer(0, "Successful load");
|
||||
|
||||
{
|
||||
isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server_.getDataSrcClientListMutex());
|
||||
// And now it should be present too.
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS,
|
||||
server_.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("example.org")).finder_->
|
||||
find(Name("www.example.org"), RRType::A())->code);
|
||||
}
|
||||
@ -321,9 +340,11 @@ TEST_F(AuthCommandTest,
|
||||
checkAnswer(1, "example.com");
|
||||
|
||||
{
|
||||
isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server_.getDataSrcClientListMutex());
|
||||
// The previous zone is not hurt in any way
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS,
|
||||
server_.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("example.org")).finder_->
|
||||
find(Name("example.org"), RRType::SOA())->code);
|
||||
}
|
||||
@ -335,16 +356,18 @@ TEST_F(AuthCommandTest,
|
||||
" \"cache-enable\": true,"
|
||||
" \"cache-zones\": [\"example.com\"]"
|
||||
"}]}"));
|
||||
EXPECT_THROW(configureDataSource(server_, config2),
|
||||
EXPECT_THROW(configureDataSource(config2),
|
||||
ConfigurableClientList::ConfigurationError);
|
||||
|
||||
result_ = execAuthServerCommand(server_, "loadzone",
|
||||
Element::fromJSON("{\"origin\": \"example.com\"}"));
|
||||
checkAnswer(1, "Unreadable");
|
||||
|
||||
isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
|
||||
isc::util::thread::Mutex::Locker locker(
|
||||
server_.getDataSrcClientListMutex());
|
||||
// The previous zone is not hurt in any way
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
|
||||
EXPECT_EQ(ZoneFinder::SUCCESS,
|
||||
server_.getDataSrcClientList(RRClass::IN())->
|
||||
find(Name("example.org")).finder_->
|
||||
find(Name("example.org"), RRType::SOA())->code);
|
||||
}
|
||||
|
@ -60,14 +60,11 @@ private:
|
||||
|
||||
typedef shared_ptr<FakeList> ListPtr;
|
||||
|
||||
// Forward declaration. We need precise definition of DatasrcConfigTest
|
||||
// to complete this function.
|
||||
void
|
||||
testConfigureDataSource(DatasrcConfigTest& test,
|
||||
const isc::data::ConstElementPtr& config)
|
||||
{
|
||||
// We use the test fixture for the Server type. This makes it possible
|
||||
// to easily fake all needed methods and look that they were called.
|
||||
configureDataSourceGeneric<DatasrcConfigTest, FakeList>(test, config);
|
||||
}
|
||||
const isc::data::ConstElementPtr& config);
|
||||
|
||||
void
|
||||
datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&,
|
||||
@ -82,26 +79,29 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&,
|
||||
class DatasrcConfigTest : public ::testing::Test {
|
||||
public:
|
||||
// These pretend to be the server
|
||||
ListPtr getClientList(const RRClass& rrclass) {
|
||||
log_ += "get " + rrclass.toText() + "\n";
|
||||
return (lists_[rrclass]);
|
||||
}
|
||||
void setClientList(const RRClass& rrclass, const ListPtr& list) {
|
||||
log_ += "set " + rrclass.toText() + " " +
|
||||
(list ? list->getConf() : "") + "\n";
|
||||
lists_[rrclass] = list;
|
||||
}
|
||||
vector<RRClass> getClientListClasses() const {
|
||||
vector<RRClass> result;
|
||||
for (std::map<RRClass, ListPtr>::const_iterator it(lists_.begin());
|
||||
it != lists_.end(); ++it) {
|
||||
result.push_back(it->first);
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
isc::util::thread::Mutex& getClientListMutex() const {
|
||||
isc::util::thread::Mutex& getDataSrcClientListMutex() const {
|
||||
return (mutex_);
|
||||
}
|
||||
void swapDataSrcClientLists(shared_ptr<std::map<dns::RRClass, ListPtr> >
|
||||
new_lists)
|
||||
{
|
||||
lists_.clear(); // first empty it
|
||||
|
||||
// Record the operation and results. Note that map elements are
|
||||
// sorted by RRClass, so the ordering should be predictable.
|
||||
for (std::map<dns::RRClass, ListPtr>::const_iterator it =
|
||||
new_lists->begin();
|
||||
it != new_lists->end();
|
||||
++it)
|
||||
{
|
||||
const RRClass rrclass = it->first;
|
||||
ListPtr list = it->second;
|
||||
log_ += "set " + rrclass.toText() + " " +
|
||||
(list ? list->getConf() : "") + "\n";
|
||||
lists_[rrclass] = list;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
DatasrcConfigTest() :
|
||||
session(ElementPtr(new ListElement), ElementPtr(new ListElement),
|
||||
@ -147,9 +147,8 @@ protected:
|
||||
session.addMessage(createCommand("config_update", config),
|
||||
"data_sources", "*");
|
||||
mccs->checkCommand();
|
||||
// Check it called the correct things (check that there's no IN yet and
|
||||
// set a new one.
|
||||
EXPECT_EQ("get IN\nset IN xxx\n", log_);
|
||||
// Check that the passed config is stored.
|
||||
EXPECT_EQ("set IN xxx\n", log_);
|
||||
EXPECT_EQ(1, lists_.size());
|
||||
}
|
||||
FakeSession session;
|
||||
@ -160,14 +159,27 @@ protected:
|
||||
mutable isc::util::thread::Mutex mutex_;
|
||||
};
|
||||
|
||||
void
|
||||
testConfigureDataSource(DatasrcConfigTest& test,
|
||||
const isc::data::ConstElementPtr& config)
|
||||
{
|
||||
// We use customized (faked lists) for the List type. This makes it
|
||||
// possible to easily look that they were called.
|
||||
shared_ptr<std::map<dns::RRClass, ListPtr> > lists =
|
||||
configureDataSourceGeneric<FakeList>(config);
|
||||
test.swapDataSrcClientLists(lists);
|
||||
}
|
||||
|
||||
// Push there a configuration with a single list.
|
||||
TEST_F(DatasrcConfigTest, createList) {
|
||||
initializeINList();
|
||||
}
|
||||
|
||||
TEST_F(DatasrcConfigTest, modifyList) {
|
||||
// First, initialize the list
|
||||
// First, initialize the list, and confirm the current config
|
||||
initializeINList();
|
||||
EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf());
|
||||
|
||||
// And now change the configuration of the list
|
||||
const ElementPtr
|
||||
config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}"));
|
||||
@ -175,9 +187,7 @@ TEST_F(DatasrcConfigTest, modifyList) {
|
||||
"*");
|
||||
log_ = "";
|
||||
mccs->checkCommand();
|
||||
// This one does not set
|
||||
EXPECT_EQ("get IN\n", log_);
|
||||
// But this should contain the yyy configuration
|
||||
// Now the new one should be installed.
|
||||
EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
|
||||
EXPECT_EQ(1, lists_.size());
|
||||
}
|
||||
@ -191,7 +201,7 @@ TEST_F(DatasrcConfigTest, multiple) {
|
||||
"*");
|
||||
mccs->checkCommand();
|
||||
// We have set commands for both classes.
|
||||
EXPECT_EQ("get CH\nset CH xxx\nget IN\nset IN yyy\n", log_);
|
||||
EXPECT_EQ("set IN yyy\nset CH xxx\n", log_);
|
||||
// We should have both there
|
||||
EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
|
||||
EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
|
||||
@ -212,9 +222,7 @@ TEST_F(DatasrcConfigTest, updateAdd) {
|
||||
"*");
|
||||
log_ = "";
|
||||
mccs->checkCommand();
|
||||
// The CH is set, IN not
|
||||
EXPECT_EQ("get CH\nset CH xxx\nget IN\n", log_);
|
||||
// But this should contain the yyy configuration
|
||||
EXPECT_EQ("set IN yyy\nset CH xxx\n", log_);
|
||||
EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
|
||||
EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
|
||||
EXPECT_EQ(2, lists_.size());
|
||||
@ -229,18 +237,18 @@ TEST_F(DatasrcConfigTest, updateDelete) {
|
||||
"*");
|
||||
log_ = "";
|
||||
mccs->checkCommand();
|
||||
EXPECT_EQ("get IN\nset IN \n", log_);
|
||||
EXPECT_FALSE(lists_[RRClass::IN()]);
|
||||
// In real auth server, the NULL one would be removed. However, we just
|
||||
// store it, so the IN bucket is still in there. This checks there's nothing
|
||||
// else.
|
||||
EXPECT_EQ(1, lists_.size());
|
||||
|
||||
// No operation takes place in the configuration, and the old one is
|
||||
// just dropped
|
||||
EXPECT_EQ("", log_);
|
||||
EXPECT_TRUE(lists_.empty());
|
||||
}
|
||||
|
||||
// Check that we can rollback an addition if something else fails
|
||||
TEST_F(DatasrcConfigTest, rollbackAddition) {
|
||||
// Check that broken new configuration doesn't break the running configuration.
|
||||
TEST_F(DatasrcConfigTest, brokenConfigForAdd) {
|
||||
initializeINList();
|
||||
// The configuration is wrong. However, the CH one will get done first.
|
||||
// The configuration is wrong. However, the CH one will be handled
|
||||
// without an error first.
|
||||
const ElementPtr
|
||||
config(buildConfig("{\"IN\": [{\"type\": 13}], "
|
||||
"\"CH\": [{\"type\": \"xxx\"}]}"));
|
||||
@ -256,8 +264,9 @@ TEST_F(DatasrcConfigTest, rollbackAddition) {
|
||||
EXPECT_FALSE(lists_[RRClass::CH()]);
|
||||
}
|
||||
|
||||
// Check that we can rollback a deletion if something else fails
|
||||
TEST_F(DatasrcConfigTest, rollbackDeletion) {
|
||||
// Similar to the previous one, but the broken config would delete part of
|
||||
// the running config.
|
||||
TEST_F(DatasrcConfigTest, brokenConfigForDelete) {
|
||||
initializeINList();
|
||||
// Put the CH there
|
||||
const ElementPtr
|
||||
@ -266,27 +275,25 @@ TEST_F(DatasrcConfigTest, rollbackDeletion) {
|
||||
testConfigureDataSource(*this, config1);
|
||||
const ElementPtr
|
||||
config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}"));
|
||||
// This would delete CH. However, the IN one fails.
|
||||
// As the deletions happen after the additions/settings
|
||||
// and there's no known way to cause an exception during the
|
||||
// deletions, it is not a true rollback, but the result should
|
||||
// be the same.
|
||||
// This would delete CH. However, the new config is broken, so it won't
|
||||
// actually apply.
|
||||
EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError);
|
||||
EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
|
||||
EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
|
||||
}
|
||||
|
||||
// Check that we can roll back configuration change if something
|
||||
// fails later on.
|
||||
TEST_F(DatasrcConfigTest, rollbackConfiguration) {
|
||||
// Similar to the previous cases, but the broken config would modify the
|
||||
// running config of one of the classes.
|
||||
TEST_F(DatasrcConfigTest, brokenConfigForModify) {
|
||||
initializeINList();
|
||||
// Put the CH there
|
||||
const ElementPtr
|
||||
config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
|
||||
"\"CH\": [{\"type\": \"xxx\"}]}"));
|
||||
testConfigureDataSource(*this, config1);
|
||||
// Now, the CH happens first. But nevertheless, it should be
|
||||
// restored to the previoeus version.
|
||||
// Now, the CH change will be handled first without an error, then
|
||||
// the change to the IN class will fail, and the none of the changes
|
||||
// will apply.
|
||||
const ElementPtr
|
||||
config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], "
|
||||
"\"CH\": [{\"type\": \"yyy\"}]}"));
|
||||
|
@ -141,6 +141,16 @@ it now. The new configuration is printed.
|
||||
% BIND10_RECEIVED_SIGNAL received signal %1
|
||||
The boss module received the given signal.
|
||||
|
||||
% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1
|
||||
The boss module tried to restart a component after it failed (crashed)
|
||||
unexpectedly, but the boss then found that the component had been removed
|
||||
from its local configuration of components to run. This is an unusal
|
||||
situation but can happen if the administrator removes the component from
|
||||
the configuration after the component's crash and before the restart time.
|
||||
The boss module simply skipped restarting that module, and the whole system
|
||||
went back to the expected state (except that the crash itself is likely
|
||||
to be a bug).
|
||||
|
||||
% BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2)
|
||||
The given process has been restarted successfully, and is now running
|
||||
with the given process id.
|
||||
@ -157,6 +167,30 @@ so BIND 10 will now shut down. The specific error is printed.
|
||||
% BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2)
|
||||
The boss module is sending a SIGKILL signal to the given process.
|
||||
|
||||
% BIND10_SEND_SIGNAL_FAIL sending %1 to %2 (PID %3) failed: %4
|
||||
The boss module sent a single (either SIGTERM or SIGKILL) to a process,
|
||||
but it failed due to some system level error. There are two major cases:
|
||||
the target process has already terminated but the boss module had sent
|
||||
the signal before it noticed the termination. In this case an error
|
||||
message should indicate something like "no such process". This can be
|
||||
safely ignored. The other case is that the boss module doesn't have
|
||||
the privilege to send a signal to the process. It can typically
|
||||
happen when the boss module started as a privileged process, spawned a
|
||||
subprocess, and then dropped the privilege. It includes the case for
|
||||
the socket creator when the boss process runs with the -u command line
|
||||
option. In this case, the boss module simply gives up to terminate
|
||||
the process explicitly because it's unlikely to succeed by keeping
|
||||
sending the signal. Although the socket creator is implemented so
|
||||
that it will terminate automatically when the boss process exits
|
||||
(and that should be the case for any other future process running with
|
||||
a higher privilege), but it's recommended to check if there's any
|
||||
remaining BIND 10 process if this message is logged. For all other
|
||||
cases, the boss module will keep sending the signal until it confirms
|
||||
all child processes terminate. Although unlikely, this could prevent
|
||||
the boss module from exiting, just keeping sending the signals. So,
|
||||
again, it's advisable to check if it really terminates when this
|
||||
message is logged.
|
||||
|
||||
% BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)
|
||||
The boss module is sending a SIGTERM signal to the given process.
|
||||
|
||||
@ -264,13 +298,6 @@ During the startup process, a number of messages are exchanged between the
|
||||
Boss process and the processes it starts. This error is output when a
|
||||
message received by the Boss process is not recognised.
|
||||
|
||||
% BIND10_START_AS_NON_ROOT_AUTH starting b10-auth as a user, not root. This might fail.
|
||||
The authoritative server is being started or restarted without root privileges.
|
||||
If the module needs these privileges, it may have problems starting.
|
||||
Note that this issue should be resolved by the pending 'socket-creator'
|
||||
process; once that has been implemented, modules should not need root
|
||||
privileges anymore. See tickets #800 and #801 for more information.
|
||||
|
||||
% BIND10_START_AS_NON_ROOT_RESOLVER starting b10-resolver as a user, not root. This might fail.
|
||||
The resolver is being started or restarted without root privileges.
|
||||
If the module needs these privileges, it may have problems starting.
|
||||
|
@ -546,8 +546,6 @@ class BoB:
|
||||
"""
|
||||
Start the Authoritative server
|
||||
"""
|
||||
if self.uid is not None and self.__started:
|
||||
logger.warn(BIND10_START_AS_NON_ROOT_AUTH)
|
||||
authargs = ['b10-auth']
|
||||
if self.verbose:
|
||||
authargs += ['-v']
|
||||
@ -693,32 +691,42 @@ class BoB:
|
||||
# from doing so
|
||||
if not self.nokill:
|
||||
# next try sending a SIGTERM
|
||||
components_to_stop = list(self.components.values())
|
||||
for component in components_to_stop:
|
||||
logger.info(BIND10_SEND_SIGTERM, component.name(), component.pid())
|
||||
try:
|
||||
component.kill()
|
||||
except OSError:
|
||||
# ignore these (usually ESRCH because the child
|
||||
# finally exited)
|
||||
pass
|
||||
# finally, send SIGKILL (unmaskable termination) until everybody dies
|
||||
self.__kill_children(False)
|
||||
# finally, send SIGKILL (unmaskable termination) until everybody
|
||||
# dies
|
||||
while self.components:
|
||||
# XXX: some delay probably useful... how much is uncertain
|
||||
time.sleep(0.1)
|
||||
self.reap_children()
|
||||
components_to_stop = list(self.components.values())
|
||||
for component in components_to_stop:
|
||||
logger.info(BIND10_SEND_SIGKILL, component.name(),
|
||||
component.pid())
|
||||
try:
|
||||
component.kill(True)
|
||||
except OSError:
|
||||
# ignore these (usually ESRCH because the child
|
||||
# finally exited)
|
||||
pass
|
||||
self.__kill_children(True)
|
||||
logger.info(BIND10_SHUTDOWN_COMPLETE)
|
||||
|
||||
def __kill_children(self, forceful):
|
||||
'''Terminate remaining subprocesses by sending a signal.
|
||||
|
||||
The forceful paramter will be passed Component.kill().
|
||||
This is a dedicated subroutine of shutdown(), just to unify two
|
||||
similar cases.
|
||||
|
||||
'''
|
||||
logmsg = BIND10_SEND_SIGKILL if forceful else BIND10_SEND_SIGTERM
|
||||
# We need to make a copy of values as the components may be modified
|
||||
# in the loop.
|
||||
for component in list(self.components.values()):
|
||||
logger.info(logmsg, component.name(), component.pid())
|
||||
try:
|
||||
component.kill(forceful)
|
||||
except OSError as ex:
|
||||
# If kill() failed due to EPERM, it doesn't make sense to
|
||||
# keep trying, so we just log the fact and forget that
|
||||
# component. Ignore other OSErrors (usually ESRCH because
|
||||
# the child finally exited)
|
||||
signame = "SIGKILL" if forceful else "SIGTERM"
|
||||
logger.info(BIND10_SEND_SIGNAL_FAIL, signame,
|
||||
component.name(), component.pid(), ex)
|
||||
if ex.errno == errno.EPERM:
|
||||
del self.components[component.pid()]
|
||||
|
||||
def _get_process_exit_status(self):
|
||||
return os.waitpid(-1, os.WNOHANG)
|
||||
|
||||
@ -739,7 +747,7 @@ class BoB:
|
||||
component = self.components.pop(pid)
|
||||
logger.info(BIND10_PROCESS_ENDED, component.name(), pid,
|
||||
exit_status)
|
||||
if component.running() and self.runnable:
|
||||
if component.is_running() and self.runnable:
|
||||
# Tell it it failed. But only if it matters (we are
|
||||
# not shutting down and the component considers itself
|
||||
# to be running.
|
||||
@ -771,7 +779,12 @@ class BoB:
|
||||
next_restart_time = None
|
||||
now = time.time()
|
||||
for component in self.components_to_restart:
|
||||
if not component.restart(now):
|
||||
# If the component was removed from the configurator between since
|
||||
# scheduled to restart, just ignore it. The object will just be
|
||||
# dropped here.
|
||||
if not self._component_configurator.has_component(component):
|
||||
logger.info(BIND10_RESTART_COMPONENT_SKIPPED, component.name())
|
||||
elif not component.restart(now):
|
||||
still_dead.append(component)
|
||||
if next_restart_time is None or\
|
||||
next_restart_time > component.get_restart_time():
|
||||
|
@ -929,7 +929,14 @@ class MockComponent:
|
||||
self.name = lambda: name
|
||||
self.pid = lambda: pid
|
||||
self.address = lambda: address
|
||||
self.restarted = False
|
||||
|
||||
def get_restart_time(self):
|
||||
return 0 # arbitrary dummy value
|
||||
|
||||
def restart(self, now):
|
||||
self.restarted = True
|
||||
return True
|
||||
|
||||
class TestBossCmd(unittest.TestCase):
|
||||
def test_ping(self):
|
||||
@ -1174,7 +1181,7 @@ class TestBossComponents(unittest.TestCase):
|
||||
# We check somewhere else that the shutdown is actually called
|
||||
# from there (the test_kills).
|
||||
|
||||
def __real_test_kill(self, nokill = False):
|
||||
def __real_test_kill(self, nokill=False, ex_on_kill=None):
|
||||
"""
|
||||
Helper function that does the actual kill functionality testing.
|
||||
"""
|
||||
@ -1188,8 +1195,23 @@ class TestBossComponents(unittest.TestCase):
|
||||
(anyway it is not told so). It does not die if it is killed
|
||||
the first time. It dies only when killed forcefully.
|
||||
"""
|
||||
def __init__(self):
|
||||
# number of kill() calls, preventing infinite loop.
|
||||
self.__call_count = 0
|
||||
|
||||
def kill(self, forceful=False):
|
||||
self.__call_count += 1
|
||||
if self.__call_count > 2:
|
||||
raise Exception('Too many calls to ImmortalComponent.kill')
|
||||
|
||||
killed.append(forceful)
|
||||
if ex_on_kill is not None:
|
||||
# If exception is given by the test, raise it here.
|
||||
# In the case of ESRCH, the process should have gone
|
||||
# somehow, so we clear the components.
|
||||
if ex_on_kill.errno == errno.ESRCH:
|
||||
bob.components = {}
|
||||
raise ex_on_kill
|
||||
if forceful:
|
||||
bob.components = {}
|
||||
def pid(self):
|
||||
@ -1217,7 +1239,10 @@ class TestBossComponents(unittest.TestCase):
|
||||
if nokill:
|
||||
self.assertEqual([], killed)
|
||||
else:
|
||||
self.assertEqual([False, True], killed)
|
||||
if ex_on_kill is not None:
|
||||
self.assertEqual([False], killed)
|
||||
else:
|
||||
self.assertEqual([False, True], killed)
|
||||
|
||||
self.assertTrue(self.__called)
|
||||
|
||||
@ -1229,6 +1254,20 @@ class TestBossComponents(unittest.TestCase):
|
||||
"""
|
||||
self.__real_test_kill()
|
||||
|
||||
def test_kill_fail(self):
|
||||
"""Test cases where kill() results in an exception due to OS error.
|
||||
|
||||
The behavior should be different for EPERM, so we test two cases.
|
||||
|
||||
"""
|
||||
|
||||
ex = OSError()
|
||||
ex.errno, ex.strerror = errno.ESRCH, 'No such process'
|
||||
self.__real_test_kill(ex_on_kill=ex)
|
||||
|
||||
ex.errno, ex.strerror = errno.EPERM, 'Operation not permitted'
|
||||
self.__real_test_kill(ex_on_kill=ex)
|
||||
|
||||
def test_nokill(self):
|
||||
"""
|
||||
Test that the boss *doesn't* kill components which don't want to
|
||||
@ -1266,6 +1305,34 @@ class TestBossComponents(unittest.TestCase):
|
||||
bob.start_all_components()
|
||||
self.__check_extended(self.__param)
|
||||
|
||||
def __setup_restart(self, bob, component):
|
||||
'''Common procedure for restarting a component used below.'''
|
||||
bob.components_to_restart = { component }
|
||||
component.restarted = False
|
||||
bob.restart_processes()
|
||||
|
||||
def test_restart_processes(self):
|
||||
'''Check some behavior on restarting processes.'''
|
||||
bob = MockBob()
|
||||
bob.runnable = True
|
||||
component = MockComponent('test', 53)
|
||||
|
||||
# A component to be restarted will actually be restarted iff it's
|
||||
# in the configurator's configuration.
|
||||
# We bruteforce the configurator internal below; ugly, but the easiest
|
||||
# way for the test.
|
||||
bob._component_configurator._components['test'] = (None, component)
|
||||
self.__setup_restart(bob, component)
|
||||
self.assertTrue(component.restarted)
|
||||
self.assertFalse(component in bob.components_to_restart)
|
||||
|
||||
# Remove the component from the configuration. It won't be restarted
|
||||
# even if scheduled, nor will remain in the to-be-restarted list.
|
||||
del bob._component_configurator._components['test']
|
||||
self.__setup_restart(bob, component)
|
||||
self.assertFalse(component.restarted)
|
||||
self.assertFalse(component in bob.components_to_restart)
|
||||
|
||||
class SocketSrvTest(unittest.TestCase):
|
||||
"""
|
||||
This tests some methods of boss related to the unix domain sockets used
|
||||
|
@ -46,6 +46,7 @@ pkglibexec_PROGRAMS = b10-dhcp6
|
||||
|
||||
b10_dhcp6_SOURCES = main.cc
|
||||
b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
|
||||
b10_dhcp6_SOURCES += config_parser.cc config_parser.h
|
||||
b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
|
||||
b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
|
||||
|
||||
@ -62,6 +63,7 @@ b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
|
||||
b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
|
||||
|
||||
|
797
src/bin/dhcp6/config_parser.cc
Normal file
797
src/bin/dhcp6/config_parser.cc
Normal file
@ -0,0 +1,797 @@
|
||||
// Copyright (C) 2010 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 <stdint.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <cc/data.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <dhcp/triplet.h>
|
||||
#include <dhcp/pool.h>
|
||||
#include <dhcp/subnet.h>
|
||||
#include <dhcp/cfgmgr.h>
|
||||
#include <dhcp6/config_parser.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::data;
|
||||
using namespace isc::asiolink;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief auxiliary type used for storing element name and its parser
|
||||
typedef pair<string, ConstElementPtr> ConfigPair;
|
||||
|
||||
/// @brief a factory method that will create a parser for a given element name
|
||||
typedef DhcpConfigParser* ParserFactory(const std::string& config_id);
|
||||
|
||||
/// @brief a collection of factories that creates parsers for specified element names
|
||||
typedef std::map<std::string, ParserFactory*> FactoryMap;
|
||||
|
||||
/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
|
||||
typedef std::map<string, uint32_t> Uint32Storage;
|
||||
|
||||
/// @brief a collection of elements that store string values
|
||||
typedef std::map<string, string> StringStorage;
|
||||
|
||||
/// @brief a collection of pools
|
||||
///
|
||||
/// That type is used as intermediate storage, when pools are parsed, but there is
|
||||
/// no subnet object created yet to store them.
|
||||
typedef std::vector<Pool6Ptr> PoolStorage;
|
||||
|
||||
/// @brief Global uint32 parameters that will be used as defaults.
|
||||
Uint32Storage uint32_defaults;
|
||||
|
||||
/// @brief global string parameters that will be used as defaults.
|
||||
StringStorage string_defaults;
|
||||
|
||||
/// @brief a dummy configuration parser
|
||||
///
|
||||
/// It is a debugging parser. It does not configure anything,
|
||||
/// will accept any configuration and will just print it out
|
||||
/// on commit. Useful for debugging existing configurations and
|
||||
/// adding new ones.
|
||||
class DebugParser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief Constructor
|
||||
///
|
||||
/// See \ref DhcpConfigParser class for details.
|
||||
///
|
||||
/// @param param_name name of the parsed parameter
|
||||
DebugParser(const std::string& param_name)
|
||||
:param_name_(param_name) {
|
||||
}
|
||||
|
||||
/// @brief builds parameter value
|
||||
///
|
||||
/// See \ref DhcpConfigParser class for details.
|
||||
///
|
||||
/// @param new_config pointer to the new configuration
|
||||
virtual void build(ConstElementPtr new_config) {
|
||||
std::cout << "Build for token: [" << param_name_ << "] = ["
|
||||
<< value_->str() << "]" << std::endl;
|
||||
value_ = new_config;
|
||||
}
|
||||
|
||||
/// @brief pretends to apply the configuration
|
||||
///
|
||||
/// This is a method required by base class. It pretends to apply the
|
||||
/// configuration, but in fact it only prints the parameter out.
|
||||
///
|
||||
/// See \ref DhcpConfigParser class for details.
|
||||
virtual void commit() {
|
||||
// Debug message. The whole DebugParser class is used only for parser
|
||||
// debugging, and is not used in production code. It is very convenient
|
||||
// to keep it around. Please do not turn this cout into logger calls.
|
||||
std::cout << "Commit for token: [" << param_name_ << "] = ["
|
||||
<< value_->str() << "]" << std::endl;
|
||||
}
|
||||
|
||||
/// @brief factory that constructs DebugParser objects
|
||||
///
|
||||
/// @param param_name name of the parameter to be parsed
|
||||
static DhcpConfigParser* Factory(const std::string& param_name) {
|
||||
return (new DebugParser(param_name));
|
||||
}
|
||||
|
||||
protected:
|
||||
/// name of the parsed parameter
|
||||
std::string param_name_;
|
||||
|
||||
/// pointer to the actual value of the parameter
|
||||
ConstElementPtr value_;
|
||||
};
|
||||
|
||||
/// @brief Configuration parser for uint32 parameters
|
||||
///
|
||||
/// This class is a generic parser that is able to handle any uint32 integer
|
||||
/// type. By default it stores the value in external global container
|
||||
/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
|
||||
/// in subnet config), it can be pointed to a different storage, using
|
||||
/// setStorage() method. This class follows the parser interface, laid out
|
||||
/// in its base class, \ref DhcpConfigParser.
|
||||
///
|
||||
/// For overview of usability of this generic purpose parser, see
|
||||
/// \ref dhcpv6-config-inherit page.
|
||||
class Uint32Parser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief constructor for Uint32Parser
|
||||
/// @param param_name name of the configuration parameter being parsed
|
||||
Uint32Parser(const std::string& param_name)
|
||||
:storage_(&uint32_defaults), param_name_(param_name) {
|
||||
}
|
||||
|
||||
/// @brief builds parameter value
|
||||
///
|
||||
/// Parses configuration entry and stores it in a storage. See
|
||||
/// \ref setStorage() for details.
|
||||
///
|
||||
/// @param value pointer to the content of parsed values
|
||||
virtual void build(ConstElementPtr value) {
|
||||
try {
|
||||
value_ = boost::lexical_cast<uint32_t>(value->str());
|
||||
} catch (const boost::bad_lexical_cast &) {
|
||||
isc_throw(BadValue, "Failed to parse value " << value->str()
|
||||
<< " as unsigned 32-bit integer.");
|
||||
}
|
||||
storage_->insert(pair<string, uint32_t>(param_name_, value_));
|
||||
}
|
||||
|
||||
/// @brief does nothing
|
||||
///
|
||||
/// This method is required for all parser. The value itself
|
||||
/// is not commited anywhere. Higher level parsers are expected to
|
||||
/// use values stored in the storage, e.g. renew-timer for a given
|
||||
/// subnet is stored in subnet-specific storage. It is not commited
|
||||
/// here, but is rather used by \ref Subnet6Parser when constructing
|
||||
/// the subnet.
|
||||
virtual void commit() {
|
||||
}
|
||||
|
||||
/// @brief factory that constructs Uint32Parser objects
|
||||
///
|
||||
/// @param param_name name of the parameter to be parsed
|
||||
static DhcpConfigParser* Factory(const std::string& param_name) {
|
||||
return (new Uint32Parser(param_name));
|
||||
}
|
||||
|
||||
/// @brief sets storage for value of this parameter
|
||||
///
|
||||
/// See \ref dhcpv6-config-inherit for details.
|
||||
///
|
||||
/// @param storage pointer to the storage container
|
||||
void setStorage(Uint32Storage* storage) {
|
||||
storage_ = storage;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// pointer to the storage, where parsed value will be stored
|
||||
Uint32Storage* storage_;
|
||||
|
||||
/// name of the parameter to be parsed
|
||||
std::string param_name_;
|
||||
|
||||
/// the actual parsed value
|
||||
uint32_t value_;
|
||||
};
|
||||
|
||||
/// @brief Configuration parser for string parameters
|
||||
///
|
||||
/// This class is a generic parser that is able to handle any string
|
||||
/// parameter. By default it stores the value in external global container
|
||||
/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
|
||||
/// in subnet config), it can be pointed to a different storage, using
|
||||
/// setStorage() method. This class follows the parser interface, laid out
|
||||
/// in its base class, \ref DhcpConfigParser.
|
||||
///
|
||||
/// For overview of usability of this generic purpose parser, see
|
||||
/// \ref dhcpv6-config-inherit page.
|
||||
class StringParser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief constructor for StringParser
|
||||
/// @param param_name name of the configuration parameter being parsed
|
||||
StringParser(const std::string& param_name)
|
||||
:storage_(&string_defaults), param_name_(param_name) {
|
||||
}
|
||||
|
||||
/// @brief parses parameter value
|
||||
///
|
||||
/// Parses configuration entry and stored it in storage. See
|
||||
/// \ref setStorage() for details.
|
||||
///
|
||||
/// @param value pointer to the content of parsed values
|
||||
virtual void build(ConstElementPtr value) {
|
||||
value_ = value->str();
|
||||
boost::erase_all(value_, "\"");
|
||||
storage_->insert(pair<string, string>(param_name_, value_));
|
||||
}
|
||||
|
||||
/// @brief does nothing
|
||||
///
|
||||
/// This method is required for all parser. The value itself
|
||||
/// is not commited anywhere. Higher level parsers are expected to
|
||||
/// use values stored in the storage, e.g. renew-timer for a given
|
||||
/// subnet is stored in subnet-specific storage. It is not commited
|
||||
/// here, but is rather used by its parent parser when constructing
|
||||
/// an object, e.g. the subnet.
|
||||
virtual void commit() {
|
||||
}
|
||||
|
||||
/// @brief factory that constructs StringParser objects
|
||||
///
|
||||
/// @param param_name name of the parameter to be parsed
|
||||
static DhcpConfigParser* Factory(const std::string& param_name) {
|
||||
return (new StringParser(param_name));
|
||||
}
|
||||
|
||||
/// @brief sets storage for value of this parameter
|
||||
///
|
||||
/// See \ref dhcpv6-config-inherit for details.
|
||||
///
|
||||
/// @param storage pointer to the storage container
|
||||
void setStorage(StringStorage* storage) {
|
||||
storage_ = storage;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// pointer to the storage, where parsed value will be stored
|
||||
StringStorage* storage_;
|
||||
|
||||
/// name of the parameter to be parsed
|
||||
std::string param_name_;
|
||||
|
||||
/// the actual parsed value
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
|
||||
/// @brief parser for interface list definition
|
||||
///
|
||||
/// This parser handles Dhcp6/interface entry.
|
||||
/// It contains a list of network interfaces that the server listens on.
|
||||
/// In particular, it can contain an entry called "all" or "any" that
|
||||
/// designates all interfaces.
|
||||
///
|
||||
/// It is useful for parsing Dhcp6/interface parameter.
|
||||
class InterfaceListConfigParser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief constructor
|
||||
///
|
||||
/// As this is a dedicated parser, it must be used to parse
|
||||
/// "interface" parameter only. All other types will throw exception.
|
||||
///
|
||||
/// @param param_name name of the configuration parameter being parsed
|
||||
InterfaceListConfigParser(const std::string& param_name) {
|
||||
if (param_name != "interface") {
|
||||
isc_throw(NotImplemented, "Internal error. Interface configuration "
|
||||
"parser called for the wrong parameter: " << param_name);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief parses parameters value
|
||||
///
|
||||
/// Parses configuration entry (list of parameters) and stores it in
|
||||
/// storage. See \ref setStorage() for details.
|
||||
///
|
||||
/// @param value pointer to the content of parsed values
|
||||
virtual void build(ConstElementPtr value) {
|
||||
BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
|
||||
interfaces_.push_back(iface->str());
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief commits interfaces list configuration
|
||||
virtual void commit() {
|
||||
/// @todo: Implement per interface listening. Currently always listening
|
||||
/// on all interfaces.
|
||||
}
|
||||
|
||||
/// @brief factory that constructs InterfaceListConfigParser objects
|
||||
///
|
||||
/// @param param_name name of the parameter to be parsed
|
||||
static DhcpConfigParser* Factory(const std::string& param_name) {
|
||||
return (new InterfaceListConfigParser(param_name));
|
||||
}
|
||||
|
||||
protected:
|
||||
/// contains list of network interfaces
|
||||
vector<string> interfaces_;
|
||||
};
|
||||
|
||||
/// @brief parser for pool definition
|
||||
///
|
||||
/// This parser handles pool definitions, i.e. a list of entries of one
|
||||
/// of two syntaxes: min-max and prefix/len. Pool6 objects are created
|
||||
/// and stored in chosen PoolStorage container.
|
||||
///
|
||||
/// As there are no default values for pool, setStorage() must be called
|
||||
/// before build(). Otherwise exception will be thrown.
|
||||
///
|
||||
/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
|
||||
class PoolParser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief constructor.
|
||||
PoolParser(const std::string& /*param_name*/)
|
||||
:pools_(NULL) {
|
||||
// ignore parameter name, it is always Dhcp6/subnet6[X]/pool
|
||||
}
|
||||
|
||||
/// @brief parses the actual list
|
||||
///
|
||||
/// This method parses the actual list of interfaces.
|
||||
/// No validation is done at this stage, everything is interpreted as
|
||||
/// interface name.
|
||||
void build(ConstElementPtr pools_list) {
|
||||
// setStorage() should have been called before build
|
||||
if (!pools_) {
|
||||
isc_throw(NotImplemented, "Parser logic error. No pool storage set,"
|
||||
" but pool parser asked to parse pools");
|
||||
}
|
||||
|
||||
BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
|
||||
|
||||
// That should be a single pool representation. It should contain
|
||||
// text is form prefix/len or first - last. Note that spaces
|
||||
// are allowed
|
||||
string txt = text_pool->stringValue();
|
||||
|
||||
// first let's remove any whitespaces
|
||||
boost::erase_all(txt, " "); // space
|
||||
boost::erase_all(txt, "\t"); // tabulation
|
||||
|
||||
// Is this prefix/len notation?
|
||||
size_t pos = txt.find("/");
|
||||
if (pos != string::npos) {
|
||||
IOAddress addr("::");
|
||||
uint8_t len = 0;
|
||||
try {
|
||||
addr = IOAddress(txt.substr(0, pos));
|
||||
|
||||
// start with the first character after /
|
||||
string prefix_len = txt.substr(pos + 1);
|
||||
|
||||
// It is lexical cast to int and then downcast to uint8_t.
|
||||
// Direct cast to uint8_t (which is really an unsigned char)
|
||||
// will result in interpreting the first digit as output
|
||||
// value and throwing exception if length is written on two
|
||||
// digits (because there are extra characters left over).
|
||||
|
||||
// No checks for values over 128. Range correctness will
|
||||
// be checked in Pool6 constructor.
|
||||
len = boost::lexical_cast<int>(prefix_len);
|
||||
} catch (...) {
|
||||
isc_throw(Dhcp6ConfigError, "Failed to parse pool "
|
||||
"definition: " << text_pool->stringValue());
|
||||
}
|
||||
|
||||
Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len));
|
||||
pools_->push_back(pool);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this min-max notation?
|
||||
pos = txt.find("-");
|
||||
if (pos != string::npos) {
|
||||
// using min-max notation
|
||||
IOAddress min(txt.substr(0,pos - 1));
|
||||
IOAddress max(txt.substr(pos + 1));
|
||||
|
||||
Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
|
||||
|
||||
pools_->push_back(pool);
|
||||
continue;
|
||||
}
|
||||
|
||||
isc_throw(Dhcp6ConfigError, "Failed to parse pool definition:"
|
||||
<< text_pool->stringValue() <<
|
||||
". Does not contain - (for min-max) nor / (prefix/len)");
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief sets storage for value of this parameter
|
||||
///
|
||||
/// See \ref dhcpv6-config-inherit for details.
|
||||
///
|
||||
/// @param storage pointer to the storage container
|
||||
void setStorage(PoolStorage* storage) {
|
||||
pools_ = storage;
|
||||
}
|
||||
|
||||
/// @brief does nothing.
|
||||
///
|
||||
/// This method is required for all parser. The value itself
|
||||
/// is not commited anywhere. Higher level parsers (for subnet) are expected
|
||||
/// to use values stored in the storage.
|
||||
virtual void commit() {}
|
||||
|
||||
/// @brief factory that constructs PoolParser objects
|
||||
///
|
||||
/// @param param_name name of the parameter to be parsed
|
||||
static DhcpConfigParser* Factory(const std::string& param_name) {
|
||||
return (new PoolParser(param_name));
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @brief pointer to the actual Pools storage
|
||||
///
|
||||
/// That is typically a storage somewhere in Subnet parser
|
||||
/// (an upper level parser).
|
||||
PoolStorage* pools_;
|
||||
};
|
||||
|
||||
/// @brief this class parses a single subnet
|
||||
///
|
||||
/// This class parses the whole subnet definition. It creates parsers
|
||||
/// for received configuration parameters as needed.
|
||||
class Subnet6ConfigParser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief constructor
|
||||
Subnet6ConfigParser(const std::string& ) {
|
||||
// The parameter should always be "subnet", but we don't check here
|
||||
// against it in case some wants to reuse this parser somewhere.
|
||||
}
|
||||
|
||||
/// @brief parses parameter value
|
||||
///
|
||||
/// @param subnet pointer to the content of subnet definition
|
||||
void build(ConstElementPtr subnet) {
|
||||
|
||||
BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
|
||||
|
||||
ParserPtr parser(createSubnet6ConfigParser(param.first));
|
||||
|
||||
// if this is an Uint32 parser, tell it to store the values
|
||||
// in values_, rather than in global storage
|
||||
boost::shared_ptr<Uint32Parser> uintParser =
|
||||
boost::dynamic_pointer_cast<Uint32Parser>(parser);
|
||||
if (uintParser) {
|
||||
uintParser->setStorage(&uint32_values_);
|
||||
} else {
|
||||
|
||||
boost::shared_ptr<StringParser> stringParser =
|
||||
boost::dynamic_pointer_cast<StringParser>(parser);
|
||||
if (stringParser) {
|
||||
stringParser->setStorage(&string_values_);
|
||||
} else {
|
||||
|
||||
boost::shared_ptr<PoolParser> poolParser =
|
||||
boost::dynamic_pointer_cast<PoolParser>(parser);
|
||||
if (poolParser) {
|
||||
poolParser->setStorage(&pools_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parser->build(param.second);
|
||||
parsers_.push_back(parser);
|
||||
}
|
||||
|
||||
// Ok, we now have subnet parsed
|
||||
}
|
||||
|
||||
/// @brief commits received configuration.
|
||||
///
|
||||
/// This method does most of the configuration. Many other parsers are just
|
||||
/// storing the values that are actually consumed here. Pool definitions
|
||||
/// created in other parsers are used here and added to newly created Subnet6
|
||||
/// objects. Subnet6 are then added to DHCP CfgMgr.
|
||||
void commit() {
|
||||
|
||||
StringStorage::const_iterator it = string_values_.find("subnet");
|
||||
if (it == string_values_.end()) {
|
||||
isc_throw(Dhcp6ConfigError,
|
||||
"Mandatory subnet definition in subnet missing");
|
||||
}
|
||||
string subnet_txt = it->second;
|
||||
boost::erase_all(subnet_txt, " ");
|
||||
boost::erase_all(subnet_txt, "\t");
|
||||
|
||||
size_t pos = subnet_txt.find("/");
|
||||
if (pos == string::npos) {
|
||||
isc_throw(Dhcp6ConfigError,
|
||||
"Invalid subnet syntax (prefix/len expected):" << it->second);
|
||||
}
|
||||
IOAddress addr(subnet_txt.substr(0, pos));
|
||||
uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
|
||||
|
||||
Triplet<uint32_t> t1 = getParam("renew-timer");
|
||||
Triplet<uint32_t> t2 = getParam("rebind-timer");
|
||||
Triplet<uint32_t> pref = getParam("preferred-lifetime");
|
||||
Triplet<uint32_t> valid = getParam("valid-lifetime");
|
||||
|
||||
/// @todo: Convert this to logger once the parser is working reliably
|
||||
stringstream tmp;
|
||||
tmp << addr.toText() << "/" << (int)len
|
||||
<< " with params t1=" << t1 << ", t2=" << t2 << ", pref="
|
||||
<< pref << ", valid=" << valid;
|
||||
|
||||
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
|
||||
|
||||
Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid));
|
||||
|
||||
for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
|
||||
subnet->addPool6(*it);
|
||||
}
|
||||
|
||||
CfgMgr::instance().addSubnet6(subnet);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief creates parsers for entries in subnet definition
|
||||
///
|
||||
/// @todo Add subnet-specific things here (e.g. subnet-specific options)
|
||||
///
|
||||
/// @param config_id name od the entry
|
||||
/// @return parser object for specified entry name
|
||||
DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
|
||||
FactoryMap factories;
|
||||
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"preferred-lifetime", Uint32Parser::Factory));
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"valid-lifetime", Uint32Parser::Factory));
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"renew-timer", Uint32Parser::Factory));
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"rebind-timer", Uint32Parser::Factory));
|
||||
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"subnet", StringParser::Factory));
|
||||
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"pool", PoolParser::Factory));
|
||||
|
||||
FactoryMap::iterator f = factories.find(config_id);
|
||||
if (f == factories.end()) {
|
||||
// Used for debugging only.
|
||||
// return new DebugParser(config_id);
|
||||
|
||||
isc_throw(NotImplemented,
|
||||
"Parser error: Subnet6 parameter not supported: "
|
||||
<< config_id);
|
||||
}
|
||||
return (f->second(config_id));
|
||||
}
|
||||
|
||||
/// @brief returns value for a given parameter (after using inheritance)
|
||||
///
|
||||
/// This method implements inheritance. For a given parameter name, it first
|
||||
/// checks if there is a global value for it and overwrites it with specific
|
||||
/// value if such value was defined in subnet.
|
||||
///
|
||||
/// @param name name of the parameter
|
||||
/// @return triplet with the parameter name
|
||||
Triplet<uint32_t> getParam(const std::string& name) {
|
||||
uint32_t value = 0;
|
||||
bool found = false;
|
||||
Uint32Storage::iterator global = uint32_defaults.find(name);
|
||||
if (global != uint32_defaults.end()) {
|
||||
value = global->second;
|
||||
found = true;
|
||||
}
|
||||
|
||||
Uint32Storage::iterator local = uint32_values_.find(name);
|
||||
if (local != uint32_values_.end()) {
|
||||
value = local->second;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
return (Triplet<uint32_t>(value));
|
||||
} else {
|
||||
isc_throw(Dhcp6ConfigError, "Mandatory parameter " << name
|
||||
<< " missing (no global default and no subnet-"
|
||||
<< "specific value)");
|
||||
}
|
||||
}
|
||||
|
||||
/// storage for subnet-specific uint32 values
|
||||
Uint32Storage uint32_values_;
|
||||
|
||||
/// storage for subnet-specific integer values
|
||||
StringStorage string_values_;
|
||||
|
||||
/// storage for pools belonging to this subnet
|
||||
PoolStorage pools_;
|
||||
|
||||
/// parsers are stored here
|
||||
ParserCollection parsers_;
|
||||
};
|
||||
|
||||
/// @brief this class parses list of subnets
|
||||
///
|
||||
/// This is a wrapper parser that handles the whole list of Subnet6
|
||||
/// definitions. It iterates over all entries and creates Subnet6ConfigParser
|
||||
/// for each entry.
|
||||
class Subnets6ListConfigParser : public DhcpConfigParser {
|
||||
public:
|
||||
|
||||
/// @brief constructor
|
||||
///
|
||||
Subnets6ListConfigParser(const std::string&) {
|
||||
/// parameter name is ignored
|
||||
}
|
||||
|
||||
/// @brief parses contents of the list
|
||||
///
|
||||
/// Iterates over all entries on the list and creates Subnet6ConfigParser
|
||||
/// for each entry.
|
||||
///
|
||||
/// @param subnets_list pointer to a list of IPv6 subnets
|
||||
void build(ConstElementPtr subnets_list) {
|
||||
|
||||
// No need to define FactoryMap here. There's only one type
|
||||
// used: Subnet6ConfigParser
|
||||
|
||||
BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
|
||||
|
||||
ParserPtr parser(new Subnet6ConfigParser("subnet"));
|
||||
parser->build(subnet);
|
||||
subnets_.push_back(parser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @brief commits subnets definitions.
|
||||
///
|
||||
/// Iterates over all Subnet6 parsers. Each parser contains definitions
|
||||
/// of a single subnet and its parameters and commits each subnet separately.
|
||||
void commit() {
|
||||
// @todo: Implement more subtle reconfiguration than toss
|
||||
// the old one and replace with the new one.
|
||||
|
||||
// remove old subnets
|
||||
CfgMgr::instance().deleteSubnets6();
|
||||
|
||||
BOOST_FOREACH(ParserPtr subnet, subnets_) {
|
||||
subnet->commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @brief Returns Subnet6ListConfigParser object
|
||||
/// @param param_name name of the parameter
|
||||
/// @return Subnets6ListConfigParser object
|
||||
static DhcpConfigParser* Factory(const std::string& param_name) {
|
||||
return (new Subnets6ListConfigParser(param_name));
|
||||
}
|
||||
|
||||
/// @brief collection of subnet parsers.
|
||||
ParserCollection subnets_;
|
||||
};
|
||||
|
||||
/// @brief creates global parsers
|
||||
///
|
||||
/// This method creates global parsers that parse global parameters, i.e.
|
||||
/// those that take format of Dhcp6/param1, Dhcp6/param2 and so forth.
|
||||
///
|
||||
/// @param config_id pointer to received global configuration entry
|
||||
/// @return parser for specified global DHCPv6 parameter
|
||||
DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
|
||||
FactoryMap factories;
|
||||
|
||||
//
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"preferred-lifetime", Uint32Parser::Factory));
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"valid-lifetime", Uint32Parser::Factory));
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"renew-timer", Uint32Parser::Factory));
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"rebind-timer", Uint32Parser::Factory));
|
||||
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"interface", InterfaceListConfigParser::Factory));
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"subnet6", Subnets6ListConfigParser::Factory));
|
||||
|
||||
factories.insert(pair<string, ParserFactory*>(
|
||||
"version", StringParser::Factory));
|
||||
|
||||
FactoryMap::iterator f = factories.find(config_id);
|
||||
if (f == factories.end()) {
|
||||
// Used for debugging only.
|
||||
// return new DebugParser(config_id);
|
||||
|
||||
isc_throw(NotImplemented,
|
||||
"Parser error: Global configuration parameter not supported: "
|
||||
<< config_id);
|
||||
}
|
||||
return (f->second(config_id));
|
||||
}
|
||||
|
||||
/// @brief configures DHCPv6 server
|
||||
///
|
||||
/// This function is called every time a new configuration is received. The extra
|
||||
/// parameter is a reference to DHCPv6 server component. It is currently not used
|
||||
/// and CfgMgr::instance() is accessed instead.
|
||||
///
|
||||
/// This method does not throw. It catches all exceptions and returns them as
|
||||
/// reconfiguration statuses. It may return the following response codes:
|
||||
/// 0 - configuration successful
|
||||
/// 1 - malformed configuration (parsing failed)
|
||||
/// 2 - logical error (parsing was successful, but the values are invalid)
|
||||
///
|
||||
/// @param config_set a new configuration for DHCPv6 server
|
||||
/// @return answer that contains result of reconfiguration
|
||||
ConstElementPtr
|
||||
configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
|
||||
if (!config_set) {
|
||||
isc_throw(Dhcp6ConfigError,
|
||||
"Null pointer is passed to configuration parser");
|
||||
}
|
||||
|
||||
/// @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());
|
||||
|
||||
ParserCollection parsers;
|
||||
try {
|
||||
BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
|
||||
|
||||
ParserPtr parser(createGlobalDhcpConfigParser(config_pair.first));
|
||||
parser->build(config_pair.second);
|
||||
parsers.push_back(parser);
|
||||
}
|
||||
} catch (const isc::Exception& ex) {
|
||||
ConstElementPtr answer = isc::config::createAnswer(1,
|
||||
string("Configuration parsing failed:") + ex.what());
|
||||
return (answer);
|
||||
} catch (...) {
|
||||
// for things like bad_cast in boost::lexical_cast
|
||||
ConstElementPtr answer = isc::config::createAnswer(1,
|
||||
string("Configuration parsing failed"));
|
||||
}
|
||||
|
||||
try {
|
||||
BOOST_FOREACH(ParserPtr parser, parsers) {
|
||||
parser->commit();
|
||||
}
|
||||
}
|
||||
catch (const isc::Exception& ex) {
|
||||
ConstElementPtr answer = isc::config::createAnswer(2,
|
||||
string("Configuration commit failed:") + ex.what());
|
||||
return (answer);
|
||||
} catch (...) {
|
||||
// for things like bad_cast in boost::lexical_cast
|
||||
ConstElementPtr answer = isc::config::createAnswer(2,
|
||||
string("Configuration commit failed"));
|
||||
}
|
||||
|
||||
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);
|
||||
|
||||
ConstElementPtr answer = isc::config::createAnswer(0, "Configuration commited.");
|
||||
return (answer);
|
||||
}
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
147
src/bin/dhcp6/config_parser.h
Normal file
147
src/bin/dhcp6/config_parser.h
Normal file
@ -0,0 +1,147 @@
|
||||
// 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 <string>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <cc/data.h>
|
||||
|
||||
#ifndef DHCP6_CONFIG_PARSER_H
|
||||
#define DHCP6_CONFIG_PARSER_H
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
class Dhcpv6Srv;
|
||||
|
||||
/// An exception that is thrown if an error occurs while configuring an
|
||||
/// \c Dhcpv6Srv object.
|
||||
class Dhcp6ConfigError : public isc::Exception {
|
||||
public:
|
||||
|
||||
/// @brief constructor
|
||||
///
|
||||
/// @param file name of the file, where exception occurred
|
||||
/// @param line line of the file, where exception occurred
|
||||
/// @param what text description of the issue that caused exception
|
||||
Dhcp6ConfigError(const char* file, size_t line, const char* what) :
|
||||
isc::Exception(file, line, what) {}
|
||||
};
|
||||
|
||||
class DhcpConfigParser {
|
||||
///
|
||||
/// \name Constructors and Destructor
|
||||
///
|
||||
/// Note: The copy constructor and the assignment operator are
|
||||
/// intentionally defined as private to make it explicit that this is a
|
||||
/// pure base class.
|
||||
//@{
|
||||
private:
|
||||
DhcpConfigParser(const DhcpConfigParser& source);
|
||||
DhcpConfigParser& operator=(const DhcpConfigParser& source);
|
||||
protected:
|
||||
/// \brief The default constructor.
|
||||
///
|
||||
/// This is intentionally defined as \c protected as this base class should
|
||||
/// never be instantiated (except as part of a derived class).
|
||||
DhcpConfigParser() {}
|
||||
public:
|
||||
/// The destructor.
|
||||
virtual ~DhcpConfigParser() {}
|
||||
//@}
|
||||
|
||||
/// \brief Prepare configuration value.
|
||||
///
|
||||
/// This method parses the "value part" of the configuration identifier
|
||||
/// that corresponds to this derived class and prepares a new value to
|
||||
/// apply to the server.
|
||||
///
|
||||
/// This method must validate the given value both in terms of syntax
|
||||
/// and semantics of the configuration, so that the server will be
|
||||
/// validly configured at the time of \c commit(). Note: the given
|
||||
/// configuration value is normally syntactically validated, but the
|
||||
/// \c build() implementation must also expect invalid input. If it
|
||||
/// detects an error it may throw an exception of a derived class
|
||||
/// of \c isc::Exception.
|
||||
///
|
||||
/// Preparing a configuration value will often require resource
|
||||
/// allocation. If it fails, it may throw a corresponding standard
|
||||
/// exception.
|
||||
///
|
||||
/// This method is not expected to be called more than once in the
|
||||
/// life of the object. Although multiple calls are not prohibited
|
||||
/// by the interface, the behavior is undefined.
|
||||
///
|
||||
/// \param config_value The configuration value for the identifier
|
||||
/// corresponding to the derived class.
|
||||
virtual void build(isc::data::ConstElementPtr config_value) = 0;
|
||||
|
||||
/// \brief Apply the prepared configuration value to the server.
|
||||
///
|
||||
/// This method is expected to be exception free, and, as a consequence,
|
||||
/// it should normally not involve resource allocation.
|
||||
/// Typically it would simply perform exception free assignment or swap
|
||||
/// operation on the value prepared in \c build().
|
||||
/// In some cases, however, it may be very difficult to meet this
|
||||
/// condition in a realistic way, while the failure case should really
|
||||
/// be very rare. In such a case it may throw, and, if the parser is
|
||||
/// called via \c configureDhcp6Server(), the caller will convert the
|
||||
/// exception as a fatal error.
|
||||
///
|
||||
/// This method is expected to be called after \c build(), and only once.
|
||||
/// The result is undefined otherwise.
|
||||
virtual void commit() = 0;
|
||||
};
|
||||
|
||||
/// @brief a pointer to configuration parser
|
||||
typedef boost::shared_ptr<DhcpConfigParser> ParserPtr;
|
||||
|
||||
/// @brief a collection of parsers
|
||||
///
|
||||
/// This container is used to store pointer to parsers for a given scope.
|
||||
typedef std::vector<ParserPtr> ParserCollection;
|
||||
|
||||
|
||||
/// \brief Configure an \c Dhcpv6Srv object with a set of configuration values.
|
||||
///
|
||||
/// This function parses configuration information stored in \c config_set
|
||||
/// and configures the \c server by applying the configuration to it.
|
||||
/// It provides the strong exception guarantee as long as the underlying
|
||||
/// derived class implementations of \c DhcpConfigParser meet the assumption,
|
||||
/// that is, it ensures that either configuration is fully applied or the
|
||||
/// state of the server is intact.
|
||||
///
|
||||
/// If a syntax or semantics level error happens during the configuration
|
||||
/// (such as malformed configuration or invalid configuration parameter),
|
||||
/// this function throws an exception of class \c Dhcp6ConfigError.
|
||||
/// If the given configuration requires resource allocation and it fails,
|
||||
/// a corresponding standard exception will be thrown.
|
||||
/// Other exceptions may also be thrown, depending on the implementation of
|
||||
/// the underlying derived class of \c Dhcp6ConfigError.
|
||||
/// In any case the strong guarantee is provided as described above except
|
||||
/// in the very rare cases where the \c commit() method of a parser throws
|
||||
/// an exception. If that happens this function converts the exception
|
||||
/// into a \c FatalError exception and rethrows it. This exception is
|
||||
/// expected to be caught at the highest level of the application to terminate
|
||||
/// the program gracefully.
|
||||
///
|
||||
/// \param server The \c Dhcpv6Srv object to be configured.
|
||||
/// \param config_set A JSON style configuration to apply to \c server.
|
||||
isc::data::ConstElementPtr
|
||||
configureDhcp6Server(Dhcpv6Srv& server,
|
||||
isc::data::ConstElementPtr config_set);
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
||||
|
||||
#endif // DHCP6_CONFIG_PARSER_H
|
@ -25,6 +25,7 @@
|
||||
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
#include <dhcp6/spec_config.h>
|
||||
#include <dhcp6/config_parser.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/buffer.h>
|
||||
@ -47,8 +48,15 @@ ConstElementPtr
|
||||
ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE)
|
||||
.arg(new_config->str());
|
||||
ConstElementPtr answer = isc::config::createAnswer(0,
|
||||
"Thank you for sending config.");
|
||||
|
||||
if (server_) {
|
||||
return (configureDhcp6Server(*server_, new_config));
|
||||
}
|
||||
|
||||
// That should never happen as we install config_handler after we instantiate
|
||||
// the server.
|
||||
ConstElementPtr answer = isc::config::createAnswer(1,
|
||||
"Configuration rejected, server is during startup/shutdown phase.");
|
||||
return (answer);
|
||||
}
|
||||
|
||||
@ -86,7 +94,7 @@ void ControlledDhcpv6Srv::sessionReader(void) {
|
||||
}
|
||||
|
||||
void ControlledDhcpv6Srv::establishSession() {
|
||||
|
||||
|
||||
string specfile;
|
||||
if (getenv("B10_FROM_BUILD")) {
|
||||
specfile = string(getenv("B10_FROM_BUILD")) +
|
||||
@ -96,15 +104,27 @@ void ControlledDhcpv6Srv::establishSession() {
|
||||
}
|
||||
|
||||
/// @todo: Check if session is not established already. Throw, if it is.
|
||||
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTING)
|
||||
.arg(specfile);
|
||||
cc_session_ = new Session(io_service_.get_io_service());
|
||||
config_session_ = new ModuleCCSession(specfile, *cc_session_,
|
||||
dhcp6ConfigHandler,
|
||||
NULL,
|
||||
dhcp6CommandHandler, false);
|
||||
config_session_->start();
|
||||
|
||||
// We initially create ModuleCCSession() without configHandler, as
|
||||
// the session module is too eager to send partial configuration.
|
||||
// We want to get the full configuration, so we explicitly call
|
||||
// getFullConfig() and then pass it to our configHandler.
|
||||
config_session_->setConfigHandler(dhcp6ConfigHandler);
|
||||
|
||||
try {
|
||||
configureDhcp6Server(*this, config_session_->getFullConfig());
|
||||
} catch (const Dhcp6ConfigError& ex) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
|
||||
}
|
||||
|
||||
/// Integrate the asynchronous I/O model of BIND 10 configuration
|
||||
/// control with the "select" model of the DHCP server. This is
|
||||
/// fully explained in \ref dhcpv6Session.
|
||||
|
79
src/bin/dhcp6/dhcp6.dox
Normal file
79
src/bin/dhcp6/dhcp6.dox
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
@page dhcpv6 DHCPv6 Server Component
|
||||
|
||||
BIND10 offers DHCPv6 server implementation. It is implemented as
|
||||
b10-dhcp6 component. Its primary code is located in
|
||||
isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
|
||||
especially lib::dhcp::Pkt6, isc::dhcp::Option and
|
||||
isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
|
||||
functionality, i.e. it is able to receive and process incoming
|
||||
requests and trasmit responses. However, it does not have database
|
||||
management, so it returns only one, hardcoded lease to whoever asks
|
||||
for it.
|
||||
|
||||
DHCPv6 server component does not support relayed traffic yet, as
|
||||
support for relay decapsulation is not implemented yet.
|
||||
|
||||
DHCPv6 server component does not use BIND10 logging yet.
|
||||
|
||||
@section dhcpv6-session BIND10 message queue integration
|
||||
|
||||
DHCPv4 server component is now integrated with BIND10 message queue.
|
||||
It follows the same principle as DHCPv4. See \ref dhcpv4Session for
|
||||
details.
|
||||
|
||||
@section dhcpv6-config-parser Configuration Parser in DHCPv6
|
||||
|
||||
b10-dhcp6 component uses BIND10 cfgmgr for commands and configuration. During
|
||||
initial configuration (See \ref
|
||||
isc::dhcp::ControlledDhcpv6Srv::establishSession()), the configuration handler
|
||||
callback is installed (see isc::dhcp::ControlledDhcpv6Srv::dhcp6ConfigHandler().
|
||||
It is called every time there is a new configuration. In particular, it is
|
||||
called every time during daemon start process. It contains a
|
||||
isc::data::ConstElementPtr to a new configuration. This simple handler calls
|
||||
\ref isc::dhcp::configureDhcp6Server() method that processes received configuration.
|
||||
|
||||
This method iterates over list of received configuration elements and creates a
|
||||
list of parsers for each received entry. Parser is an object that is derived
|
||||
from a \ref isc::dhcp::Dhcp6ConfigParser class. Once a parser is created
|
||||
(constructor), its value is set (using build() method). Once all parsers are
|
||||
build, the configuration is then applied ("commited") and commit() method is
|
||||
called.
|
||||
|
||||
All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some of them
|
||||
are generic (e.g. \ref isc::dhcp::Uint32Parser that is able to handle any
|
||||
unsigned 32 bit integer), but some are very specialized (e.g. \ref
|
||||
isc::dhcp::Subnets6ListConfigParser parses definitions of Subnet6 lists). In
|
||||
some cases, e.g. subnet6 definitions, the configuration entry is not a simple
|
||||
value, but a map or a list itself. In such case, the parser iterates over all
|
||||
elements and creates parsers for a given scope. This process may be repeated
|
||||
(sort of) recursively.
|
||||
|
||||
@section dhcpv6-config-inherit DHCPv6 Configuration Inheritance
|
||||
|
||||
One notable useful features of DHCP configuration is its parameter inheritance.
|
||||
For example, renew-timer value may be specified at a global scope and it then
|
||||
applies to all subnets. However, some subnets may have it overwritten with more
|
||||
specific values that takes precedence over global values that are considered
|
||||
defaults. Some parsers (e.g. \ref isc::dhcp::Uint32Parser and \ref
|
||||
isc::dhcp::StringParser) implement that inheritance. By default, they store
|
||||
values in global uint32_defaults and string_defaults storages. However, it is
|
||||
possible to instruct them to store parsed values in more specific
|
||||
storages. That capability is used, e.g. in \ref isc::dhcp::Subnet6ConfigParser
|
||||
that has its own storage that is unique for each subnet. Finally, during commit
|
||||
phase (commit() method), appropriate parsers can use apply parameter inheritance.
|
||||
|
||||
Debugging configuration parser may be confusing. Therefore there is a special
|
||||
class called \ref isc::dhcp::DummyParser. It does not configure anything, but just
|
||||
accepts any parameter of any type. If requested to commit configuration, it will
|
||||
print out received parameter name and its value. This class is not currently used,
|
||||
but it is convenient to have it every time a new parameter is added to DHCP
|
||||
configuration. For that purpose it should be left in the code.
|
||||
|
||||
Parameter inheritance is done during reconfiguration phase, as reconfigurations
|
||||
are rare, so extra logic here is not a problem. On the other hand, values of
|
||||
those parameters may be used thousands times per second, so its use must be as
|
||||
simple as possible. In fact, currently the code has to call Subnet6->getT1() and
|
||||
do not implement any fancy inheritance logic.
|
||||
|
||||
*/
|
@ -4,9 +4,97 @@
|
||||
"module_description": "DHCPv6 server daemon",
|
||||
"config_data": [
|
||||
{ "item_name": "interface",
|
||||
"item_type": "string",
|
||||
"item_type": "list",
|
||||
"item_optional": false,
|
||||
"item_default": "eth0"
|
||||
"item_default": [ "all" ],
|
||||
"list_item_spec":
|
||||
{
|
||||
"item_name": "interface_name",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": "all"
|
||||
}
|
||||
} ,
|
||||
|
||||
{ "item_name": "renew-timer",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 1000
|
||||
},
|
||||
|
||||
{ "item_name": "rebind-timer",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 2000
|
||||
},
|
||||
|
||||
{ "item_name": "preferred-lifetime",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 3000
|
||||
},
|
||||
|
||||
{ "item_name": "valid-lifetime",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 4000
|
||||
},
|
||||
|
||||
{ "item_name": "subnet6",
|
||||
"item_type": "list",
|
||||
"item_optional": false,
|
||||
"item_default": [],
|
||||
"list_item_spec":
|
||||
{
|
||||
"item_name": "single-subnet6",
|
||||
"item_type": "map",
|
||||
"item_optional": false,
|
||||
"item_default": {},
|
||||
"map_item_spec": [
|
||||
|
||||
{ "item_name": "subnet",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": ""
|
||||
},
|
||||
|
||||
{ "item_name": "renew-timer",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 1000
|
||||
},
|
||||
|
||||
{ "item_name": "rebind-timer",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 2000
|
||||
},
|
||||
|
||||
{ "item_name": "preferred-lifetime",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 3000
|
||||
},
|
||||
|
||||
{ "item_name": "valid-lifetime",
|
||||
"item_type": "integer",
|
||||
"item_optional": false,
|
||||
"item_default": 7200
|
||||
},
|
||||
{ "item_name": "pool",
|
||||
"item_type": "list",
|
||||
"item_optional": false,
|
||||
"item_default": [],
|
||||
"list_item_spec":
|
||||
{
|
||||
"item_name": "type",
|
||||
"item_type": "string",
|
||||
"item_optional": false,
|
||||
"item_default": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
|
@ -109,3 +109,23 @@ processed any command-line switches and is starting.
|
||||
This is a debug message issued during the IPv6 DHCP server startup.
|
||||
It lists some information about the parameters with which the server
|
||||
is running.
|
||||
|
||||
% DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1
|
||||
This critical error message indicates that the initial DHCPv6
|
||||
configuration has failed. The server will start, but nothing will be
|
||||
served until the configuration has been corrected.
|
||||
|
||||
% DHCP6_CONFIG_START DHCPv6 server is processing the following configuration: %1
|
||||
This is a debug message that is issued every time the server receives a
|
||||
configuration. That happens start up and also when a server configuration
|
||||
change is committed by the administrator.
|
||||
|
||||
% DHCP6_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1
|
||||
This is an informational message reporting that the configuration has
|
||||
been extended to include the specified subnet.
|
||||
|
||||
% DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1
|
||||
This is an informational message announcing the successful processing of a
|
||||
new configuration. it is output during server startup, and when an updated
|
||||
configuration is committed by the administrator. Additional information
|
||||
may be provided.
|
||||
|
@ -42,6 +42,13 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
|
||||
const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
|
||||
|
||||
Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
|
||||
if (port == 0) {
|
||||
// used for testing purposes. Some tests, e.g. configuration parser,
|
||||
// require Dhcpv6Srv object, but they don't really need it to do
|
||||
// anything. This speed up and simplifies the tests.
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
|
||||
|
||||
// First call to instance() will create IfaceMgr (it's a singleton)
|
||||
|
@ -46,9 +46,11 @@ TESTS += dhcp6_unittests
|
||||
dhcp6_unittests_SOURCES = dhcp6_unittests.cc
|
||||
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
|
||||
dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
|
||||
dhcp6_unittests_SOURCES += config_parser_unittest.cc
|
||||
dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
|
||||
dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
|
||||
dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
|
||||
dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
|
||||
nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
|
||||
|
||||
if USE_CLANGPP
|
||||
@ -62,6 +64,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
dhcp6_unittests_LDADD = $(GTEST_LDADD)
|
||||
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
||||
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
|
||||
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
|
||||
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
||||
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
|
||||
|
243
src/bin/dhcp6/tests/config_parser_unittest.cc
Normal file
243
src/bin/dhcp6/tests/config_parser_unittest.cc
Normal file
@ -0,0 +1,243 @@
|
||||
// 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 <config.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <dhcp6/dhcp6_srv.h>
|
||||
#include <dhcp6/config_parser.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <dhcp/subnet.h>
|
||||
#include <dhcp/cfgmgr.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::data;
|
||||
using namespace isc::config;
|
||||
|
||||
namespace {
|
||||
|
||||
class Dhcp6ParserTest : public ::testing::Test {
|
||||
public:
|
||||
Dhcp6ParserTest()
|
||||
:rcode_(-1) {
|
||||
// Open port 0 means to not do anything at all. We don't want to
|
||||
// deal with sockets here, just check if configuration handling
|
||||
// is sane.
|
||||
srv_ = new Dhcpv6Srv(0);
|
||||
}
|
||||
|
||||
~Dhcp6ParserTest() {
|
||||
delete srv_;
|
||||
};
|
||||
|
||||
Dhcpv6Srv* srv_;
|
||||
|
||||
int rcode_;
|
||||
ConstElementPtr comment_;
|
||||
};
|
||||
|
||||
// Goal of this test is a verification if a very simple config update
|
||||
// with just a bumped version number. That's the simplest possible
|
||||
// config update.
|
||||
TEST_F(Dhcp6ParserTest, version) {
|
||||
|
||||
ConstElementPtr x;
|
||||
|
||||
EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
|
||||
Element::fromJSON("{\"version\": 0}")));
|
||||
|
||||
// returned value must be 0 (configuration accepted)
|
||||
ASSERT_TRUE(x);
|
||||
comment_ = parseAnswer(rcode_, x);
|
||||
EXPECT_EQ(0, rcode_);
|
||||
}
|
||||
|
||||
/// The goal of this test is to verify that the code accepts only
|
||||
/// valid commands and malformed or unsupported parameters are rejected.
|
||||
TEST_F(Dhcp6ParserTest, bogus_command) {
|
||||
|
||||
ConstElementPtr x;
|
||||
|
||||
EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
|
||||
Element::fromJSON("{\"bogus\": 5}")));
|
||||
|
||||
// returned value must be 1 (configuration parse error)
|
||||
ASSERT_TRUE(x);
|
||||
comment_ = parseAnswer(rcode_, x);
|
||||
EXPECT_EQ(1, rcode_);
|
||||
}
|
||||
|
||||
/// The goal of this test is to verify if wrongly defined subnet will
|
||||
/// be rejected. Properly defined subnet must include at least one
|
||||
/// pool definition.
|
||||
TEST_F(Dhcp6ParserTest, empty_subnet) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(*srv_,
|
||||
Element::fromJSON("{ \"interface\": [ \"all\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ ], "
|
||||
"\"valid-lifetime\": 4000 }")));
|
||||
|
||||
// returned value should be 0 (success)
|
||||
ASSERT_TRUE(status);
|
||||
comment_ = parseAnswer(rcode_, status);
|
||||
EXPECT_EQ(0, rcode_);
|
||||
}
|
||||
|
||||
/// The goal of this test is to verify if defined subnet uses global
|
||||
/// parameter timer definitions.
|
||||
TEST_F(Dhcp6ParserTest, subnet_global_defaults) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
|
||||
" \"subnet\": \"2001:db8:1::/64\" } ],"
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
cout << config << endl;
|
||||
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
|
||||
|
||||
// check if returned status is OK
|
||||
ASSERT_TRUE(status);
|
||||
comment_ = parseAnswer(rcode_, status);
|
||||
EXPECT_EQ(0, rcode_);
|
||||
|
||||
// Now check if the configuration was indeed handled and we have
|
||||
// expected pool configured.
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
|
||||
ASSERT_TRUE(subnet);
|
||||
EXPECT_EQ(1000, subnet->getT1());
|
||||
EXPECT_EQ(2000, subnet->getT2());
|
||||
EXPECT_EQ(3000, subnet->getPreferred());
|
||||
EXPECT_EQ(4000, subnet->getValid());
|
||||
}
|
||||
|
||||
// This test checks if it is possible to override global values
|
||||
// on a per subnet basis.
|
||||
TEST_F(Dhcp6ParserTest, subnet_local) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
|
||||
" \"renew-timer\": 1, "
|
||||
" \"rebind-timer\": 2, "
|
||||
" \"preferred-lifetime\": 3,"
|
||||
" \"valid-lifetime\": 4,"
|
||||
" \"subnet\": \"2001:db8:1::/64\" } ],"
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
cout << config << endl;
|
||||
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
|
||||
|
||||
// returned value should be 0 (configuration success)
|
||||
ASSERT_TRUE(status);
|
||||
comment_ = parseAnswer(rcode_, status);
|
||||
EXPECT_EQ(0, rcode_);
|
||||
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
|
||||
ASSERT_TRUE(subnet);
|
||||
EXPECT_EQ(1, subnet->getT1());
|
||||
EXPECT_EQ(2, subnet->getT2());
|
||||
EXPECT_EQ(3, subnet->getPreferred());
|
||||
EXPECT_EQ(4, subnet->getValid());
|
||||
}
|
||||
|
||||
// Test verifies that a subnet with pool values that do not belong to that
|
||||
// pool are rejected.
|
||||
TEST_F(Dhcp6ParserTest, pool_out_of_subnet) {
|
||||
|
||||
ConstElementPtr status;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"pool\": [ \"4001:db8:1::/80\" ],"
|
||||
" \"subnet\": \"2001:db8:1::/64\" } ],"
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
cout << config << endl;
|
||||
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
|
||||
|
||||
// returned value must be 2 (values error)
|
||||
// as the pool does not belong to that subnet
|
||||
ASSERT_TRUE(status);
|
||||
comment_ = parseAnswer(rcode_, status);
|
||||
EXPECT_EQ(2, rcode_);
|
||||
}
|
||||
|
||||
// Goal of this test is to verify if pools can be defined
|
||||
// using prefix/length notation. There is no separate test for min-max
|
||||
// notation as it was tested in several previous tests.
|
||||
TEST_F(Dhcp6ParserTest, pool_prefix_len) {
|
||||
|
||||
ConstElementPtr x;
|
||||
|
||||
string config = "{ \"interface\": [ \"all\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"pool\": [ \"2001:db8:1::/80\" ],"
|
||||
" \"subnet\": \"2001:db8:1::/64\" } ],"
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
cout << config << endl;
|
||||
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
|
||||
|
||||
// returned value must be 1 (configuration parse error)
|
||||
ASSERT_TRUE(x);
|
||||
comment_ = parseAnswer(rcode_, x);
|
||||
EXPECT_EQ(0, rcode_);
|
||||
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
|
||||
ASSERT_TRUE(subnet);
|
||||
EXPECT_EQ(1000, subnet->getT1());
|
||||
EXPECT_EQ(2000, subnet->getT2());
|
||||
EXPECT_EQ(3000, subnet->getPreferred());
|
||||
EXPECT_EQ(4000, subnet->getValid());
|
||||
}
|
||||
|
||||
};
|
@ -57,32 +57,32 @@ Element::toWire(std::ostream& ss) const {
|
||||
}
|
||||
|
||||
bool
|
||||
Element::getValue(long int&) {
|
||||
Element::getValue(long int&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
Element::getValue(double&) {
|
||||
Element::getValue(double&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
Element::getValue(bool&) {
|
||||
Element::getValue(bool&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
Element::getValue(std::string&) {
|
||||
Element::getValue(std::string&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
Element::getValue(std::vector<ConstElementPtr>&) {
|
||||
Element::getValue(std::vector<ConstElementPtr>&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
Element::getValue(std::map<std::string, ConstElementPtr>&) {
|
||||
Element::getValue(std::map<std::string, ConstElementPtr>&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ Element::find(const std::string&) const {
|
||||
}
|
||||
|
||||
bool
|
||||
Element::find(const std::string&, ConstElementPtr) const {
|
||||
Element::find(const std::string&, ConstElementPtr&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
@ -812,7 +812,7 @@ MapElement::set(const std::string& key, ConstElementPtr value) {
|
||||
}
|
||||
|
||||
bool
|
||||
MapElement::find(const std::string& id, ConstElementPtr t) const {
|
||||
MapElement::find(const std::string& id, ConstElementPtr& t) const {
|
||||
try {
|
||||
ConstElementPtr p = find(id);
|
||||
if (p) {
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
/// the type in question.
|
||||
///
|
||||
class Element {
|
||||
|
||||
|
||||
private:
|
||||
// technically the type could be omitted; is it useful?
|
||||
// should we remove it or replace it with a pure virtual
|
||||
@ -112,7 +112,7 @@ public:
|
||||
/// \returns true if the other ElementPtr has the same type and
|
||||
/// value
|
||||
virtual bool equals(const Element& other) const = 0;
|
||||
|
||||
|
||||
/// Converts the Element to JSON format and appends it to
|
||||
/// the given stringstream.
|
||||
virtual void toJSON(std::ostream& ss) const = 0;
|
||||
@ -152,12 +152,12 @@ public:
|
||||
/// data to the given reference and returning true
|
||||
///
|
||||
//@{
|
||||
virtual bool getValue(long int& t);
|
||||
virtual bool getValue(double& t);
|
||||
virtual bool getValue(bool& t);
|
||||
virtual bool getValue(std::string& t);
|
||||
virtual bool getValue(std::vector<ConstElementPtr>& t);
|
||||
virtual bool getValue(std::map<std::string, ConstElementPtr>& t);
|
||||
virtual bool getValue(long int& t) const;
|
||||
virtual bool getValue(double& t) const;
|
||||
virtual bool getValue(bool& t) const;
|
||||
virtual bool getValue(std::string& t) const;
|
||||
virtual bool getValue(std::vector<ConstElementPtr>& t) const;
|
||||
virtual bool getValue(std::map<std::string, ConstElementPtr>& t) const;
|
||||
//@}
|
||||
|
||||
///
|
||||
@ -209,7 +209,7 @@ public:
|
||||
virtual size_t size() const;
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/// \name MapElement functions
|
||||
///
|
||||
/// \brief If the Element on which these functions are called are not
|
||||
@ -253,12 +253,12 @@ public:
|
||||
/// \param identifier The identifier of the element to find
|
||||
/// \param t Reference to store the resulting ElementPtr, if found.
|
||||
/// \return true if the element was found, false if not.
|
||||
virtual bool find(const std::string& identifier, ConstElementPtr t) const;
|
||||
virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
|
||||
//@}
|
||||
|
||||
|
||||
/// \name Factory functions
|
||||
|
||||
|
||||
// TODO: should we move all factory functions to a different class
|
||||
// so as not to burden the Element base with too many functions?
|
||||
// and/or perhaps even to a separate header?
|
||||
@ -349,7 +349,7 @@ public:
|
||||
/// These function pparse the wireformat at the given stringstream
|
||||
/// (of the given length). If there is a parse error an exception
|
||||
/// of the type isc::cc::DecodeError is raised.
|
||||
|
||||
|
||||
//@{
|
||||
/// Creates an Element from the wire format in the given
|
||||
/// stringstream of the given length.
|
||||
@ -378,7 +378,7 @@ public:
|
||||
IntElement(long int v) : Element(integer), i(v) { }
|
||||
long int intValue() const { return (i); }
|
||||
using Element::getValue;
|
||||
bool getValue(long int& t) { t = i; return (true); }
|
||||
bool getValue(long int& t) const { t = i; return (true); }
|
||||
using Element::setValue;
|
||||
bool setValue(const long int v) { i = v; return (true); }
|
||||
void toJSON(std::ostream& ss) const;
|
||||
@ -392,7 +392,7 @@ public:
|
||||
DoubleElement(double v) : Element(real), d(v) {};
|
||||
double doubleValue() const { return (d); }
|
||||
using Element::getValue;
|
||||
bool getValue(double& t) { t = d; return (true); }
|
||||
bool getValue(double& t) const { t = d; return (true); }
|
||||
using Element::setValue;
|
||||
bool setValue(const double v) { d = v; return (true); }
|
||||
void toJSON(std::ostream& ss) const;
|
||||
@ -406,7 +406,7 @@ public:
|
||||
BoolElement(const bool v) : Element(boolean), b(v) {};
|
||||
bool boolValue() const { return (b); }
|
||||
using Element::getValue;
|
||||
bool getValue(bool& t) { t = b; return (true); }
|
||||
bool getValue(bool& t) const { t = b; return (true); }
|
||||
using Element::setValue;
|
||||
bool setValue(const bool v) { b = v; return (true); }
|
||||
void toJSON(std::ostream& ss) const;
|
||||
@ -427,7 +427,7 @@ public:
|
||||
StringElement(std::string v) : Element(string), s(v) {};
|
||||
std::string stringValue() const { return (s); }
|
||||
using Element::getValue;
|
||||
bool getValue(std::string& t) { t = s; return (true); }
|
||||
bool getValue(std::string& t) const { t = s; return (true); }
|
||||
using Element::setValue;
|
||||
bool setValue(const std::string& v) { s = v; return (true); }
|
||||
void toJSON(std::ostream& ss) const;
|
||||
@ -441,7 +441,7 @@ public:
|
||||
ListElement() : Element(list) {}
|
||||
const std::vector<ConstElementPtr>& listValue() const { return (l); }
|
||||
using Element::getValue;
|
||||
bool getValue(std::vector<ConstElementPtr>& t) {
|
||||
bool getValue(std::vector<ConstElementPtr>& t) const {
|
||||
t = l;
|
||||
return (true);
|
||||
}
|
||||
@ -474,7 +474,7 @@ public:
|
||||
return (m);
|
||||
}
|
||||
using Element::getValue;
|
||||
bool getValue(std::map<std::string, ConstElementPtr>& t) {
|
||||
bool getValue(std::map<std::string, ConstElementPtr>& t) const {
|
||||
t = m;
|
||||
return (true);
|
||||
}
|
||||
@ -495,7 +495,7 @@ public:
|
||||
return (m.find(s) != m.end());
|
||||
}
|
||||
void toJSON(std::ostream& ss) const;
|
||||
|
||||
|
||||
// we should name the two finds better...
|
||||
// find the element at id; raises TypeError if one of the
|
||||
// elements at path except the one we're looking for is not a
|
||||
@ -507,7 +507,7 @@ public:
|
||||
// returns true if found, or false if not found (either because
|
||||
// it doesnt exist or one of the elements in the path is not
|
||||
// a MapElement)
|
||||
bool find(const std::string& id, ConstElementPtr t) const;
|
||||
bool find(const std::string& id, ConstElementPtr& t) const;
|
||||
|
||||
bool equals(const Element& other) const;
|
||||
};
|
||||
@ -569,6 +569,6 @@ bool operator!=(const Element& a, const Element& b);
|
||||
} }
|
||||
#endif // _ISC_DATA_H
|
||||
|
||||
// Local Variables:
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
// End:
|
||||
// End:
|
||||
|
@ -112,7 +112,7 @@ TEST(Element, from_and_to_json) {
|
||||
std::string s = std::string(pe.what());
|
||||
EXPECT_EQ("String expected in <string>:1:3", s);
|
||||
}
|
||||
|
||||
|
||||
sv.clear();
|
||||
sv.push_back("{1}");
|
||||
//ElementPtr ep = Element::fromJSON("\"aaa\nbbb\"err");
|
||||
@ -172,14 +172,14 @@ TEST(Element, from_and_to_json) {
|
||||
|
||||
}
|
||||
|
||||
TEST(Element, create_and_value_throws) {
|
||||
// this test checks whether elements throw exceptions if the
|
||||
// incorrect type is requested
|
||||
ElementPtr el;
|
||||
template <typename T>
|
||||
void
|
||||
testGetValueInt() {
|
||||
T el;
|
||||
long int i;
|
||||
double d;
|
||||
bool b;
|
||||
std::string s("asdf");
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
@ -196,7 +196,161 @@ TEST(Element, create_and_value_throws) {
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ(i, 1);
|
||||
EXPECT_EQ(1, i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
testGetValueDouble() {
|
||||
T el;
|
||||
long int i;
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::create(1.1);
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->doubleValue());
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_TRUE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ(1.1, d);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
testGetValueBool() {
|
||||
T el;
|
||||
long int i;
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::create(true);
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->boolValue());
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_TRUE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ(true, b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
testGetValueString() {
|
||||
T el;
|
||||
long int i;
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::create("foo");
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->stringValue());
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_TRUE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ("foo", s);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
testGetValueList() {
|
||||
T el;
|
||||
long int i;
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::createList();
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->listValue());
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_TRUE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ("[ ]", el->str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
testGetValueMap() {
|
||||
T el;
|
||||
long int i;
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::createMap();
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->mapValue());
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_TRUE(el->getValue(m));
|
||||
EXPECT_EQ("{ }", el->str());
|
||||
}
|
||||
|
||||
TEST(Element, create_and_value_throws) {
|
||||
// this test checks whether elements throw exceptions if the
|
||||
// incorrect type is requested
|
||||
ElementPtr el;
|
||||
ConstElementPtr cel;
|
||||
long int i = 0;
|
||||
double d = 0.0;
|
||||
bool b = false;
|
||||
std::string s("asdf");
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
ConstElementPtr tmp;
|
||||
|
||||
testGetValueInt<ElementPtr>();
|
||||
testGetValueInt<ConstElementPtr>();
|
||||
|
||||
el = Element::create(1);
|
||||
i = 2;
|
||||
EXPECT_TRUE(el->setValue(i));
|
||||
EXPECT_EQ(2, el->intValue());
|
||||
@ -214,24 +368,12 @@ TEST(Element, create_and_value_throws) {
|
||||
EXPECT_THROW(el->set("foo", el), TypeError);
|
||||
EXPECT_THROW(el->remove("foo"), TypeError);
|
||||
EXPECT_THROW(el->contains("foo"), TypeError);
|
||||
ConstElementPtr tmp;
|
||||
EXPECT_FALSE(el->find("foo", tmp));
|
||||
|
||||
|
||||
testGetValueDouble<ElementPtr>();
|
||||
testGetValueDouble<ConstElementPtr>();
|
||||
|
||||
el = Element::create(1.1);
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->doubleValue());
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_TRUE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ(d, 1.1);
|
||||
d = 2.2;
|
||||
EXPECT_TRUE(el->setValue(d));
|
||||
EXPECT_EQ(2.2, el->doubleValue());
|
||||
@ -240,75 +382,77 @@ TEST(Element, create_and_value_throws) {
|
||||
EXPECT_FALSE(el->setValue(s));
|
||||
EXPECT_FALSE(el->setValue(v));
|
||||
EXPECT_FALSE(el->setValue(m));
|
||||
EXPECT_THROW(el->get(1), TypeError);
|
||||
EXPECT_THROW(el->set(1, el), TypeError);
|
||||
EXPECT_THROW(el->add(el), TypeError);
|
||||
EXPECT_THROW(el->remove(1), TypeError);
|
||||
EXPECT_THROW(el->size(), TypeError);
|
||||
EXPECT_THROW(el->get("foo"), TypeError);
|
||||
EXPECT_THROW(el->set("foo", el), TypeError);
|
||||
EXPECT_THROW(el->remove("foo"), TypeError);
|
||||
EXPECT_THROW(el->contains("foo"), TypeError);
|
||||
EXPECT_FALSE(el->find("foo", tmp));
|
||||
|
||||
testGetValueBool<ElementPtr>();
|
||||
testGetValueBool<ConstElementPtr>();
|
||||
|
||||
el = Element::create(true);
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->boolValue());
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_TRUE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ(b, true);
|
||||
b = false;
|
||||
EXPECT_TRUE(el->setValue(b));
|
||||
EXPECT_FALSE(el->boolValue());
|
||||
EXPECT_FALSE(el->setValue(i));
|
||||
EXPECT_FALSE(el->setValue(d));
|
||||
EXPECT_FALSE(el->setValue(s));
|
||||
EXPECT_FALSE(el->setValue(v));
|
||||
EXPECT_FALSE(el->setValue(m));
|
||||
EXPECT_THROW(el->get(1), TypeError);
|
||||
EXPECT_THROW(el->set(1, el), TypeError);
|
||||
EXPECT_THROW(el->add(el), TypeError);
|
||||
EXPECT_THROW(el->remove(1), TypeError);
|
||||
EXPECT_THROW(el->size(), TypeError);
|
||||
EXPECT_THROW(el->get("foo"), TypeError);
|
||||
EXPECT_THROW(el->set("foo", el), TypeError);
|
||||
EXPECT_THROW(el->remove("foo"), TypeError);
|
||||
EXPECT_THROW(el->contains("foo"), TypeError);
|
||||
EXPECT_FALSE(el->find("foo", tmp));
|
||||
|
||||
testGetValueString<ElementPtr>();
|
||||
testGetValueString<ConstElementPtr>();
|
||||
|
||||
el = Element::create("foo");
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->stringValue());
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_TRUE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ(s, "foo");
|
||||
s = "bar";
|
||||
EXPECT_TRUE(el->setValue(s));
|
||||
EXPECT_EQ("bar", el->stringValue());
|
||||
EXPECT_FALSE(el->setValue(i));
|
||||
EXPECT_FALSE(el->setValue(b));
|
||||
EXPECT_FALSE(el->setValue(d));
|
||||
EXPECT_FALSE(el->setValue(v));
|
||||
EXPECT_FALSE(el->setValue(m));
|
||||
EXPECT_THROW(el->get(1), TypeError);
|
||||
EXPECT_THROW(el->set(1, el), TypeError);
|
||||
EXPECT_THROW(el->add(el), TypeError);
|
||||
EXPECT_THROW(el->remove(1), TypeError);
|
||||
EXPECT_THROW(el->size(), TypeError);
|
||||
EXPECT_THROW(el->get("foo"), TypeError);
|
||||
EXPECT_THROW(el->set("foo", el), TypeError);
|
||||
EXPECT_THROW(el->remove("foo"), TypeError);
|
||||
EXPECT_THROW(el->contains("foo"), TypeError);
|
||||
EXPECT_FALSE(el->find("foo", tmp));
|
||||
|
||||
testGetValueList<ElementPtr>();
|
||||
testGetValueList<ConstElementPtr>();
|
||||
|
||||
el = Element::createList();
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->listValue());
|
||||
EXPECT_THROW(el->mapValue(), TypeError);
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_TRUE(el->getValue(v));
|
||||
EXPECT_FALSE(el->getValue(m));
|
||||
EXPECT_EQ("[ ]", el->str());
|
||||
v.push_back(Element::create(1));
|
||||
EXPECT_TRUE(el->setValue(v));
|
||||
EXPECT_EQ("[ 1 ]", el->str());
|
||||
|
||||
el = Element::createMap();
|
||||
EXPECT_THROW(el->intValue(), TypeError);
|
||||
EXPECT_THROW(el->doubleValue(), TypeError);
|
||||
EXPECT_THROW(el->boolValue(), TypeError);
|
||||
EXPECT_THROW(el->stringValue(), TypeError);
|
||||
EXPECT_THROW(el->listValue(), TypeError);
|
||||
EXPECT_NO_THROW(el->mapValue());
|
||||
EXPECT_FALSE(el->getValue(i));
|
||||
EXPECT_FALSE(el->getValue(d));
|
||||
EXPECT_FALSE(el->getValue(b));
|
||||
EXPECT_FALSE(el->getValue(s));
|
||||
EXPECT_FALSE(el->getValue(v));
|
||||
EXPECT_TRUE(el->getValue(m));
|
||||
testGetValueMap<ElementPtr>();
|
||||
testGetValueMap<ConstElementPtr>();
|
||||
|
||||
el = Element::createMap();
|
||||
EXPECT_NO_THROW(el->set("foo", Element::create("bar")));
|
||||
EXPECT_EQ("{ \"foo\": \"bar\" }", el->str());
|
||||
}
|
||||
|
||||
// Helper for escape check; it puts the given string in a StringElement,
|
||||
@ -382,7 +526,7 @@ TEST(Element, MapElement) {
|
||||
// this function checks the specific functions for ListElements
|
||||
ElementPtr el = Element::fromJSON("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }");
|
||||
ConstElementPtr el2;
|
||||
|
||||
|
||||
EXPECT_EQ(el->get("name")->stringValue(), "foo");
|
||||
EXPECT_EQ(el->get("value2")->getType(), Element::map);
|
||||
|
||||
@ -396,11 +540,12 @@ TEST(Element, MapElement) {
|
||||
|
||||
EXPECT_EQ(el->find("value2/number")->intValue(), 42);
|
||||
EXPECT_TRUE(isNull(el->find("value2/nothing/")));
|
||||
|
||||
|
||||
EXPECT_EQ(el->find("value1")->stringValue(), "bar");
|
||||
EXPECT_EQ(el->find("value1/")->stringValue(), "bar");
|
||||
|
||||
|
||||
EXPECT_TRUE(el->find("value1", el2));
|
||||
EXPECT_EQ("bar", el2->stringValue());
|
||||
EXPECT_FALSE(el->find("name/error", el2));
|
||||
|
||||
// A map element whose (only) element has the maximum length of tag.
|
||||
@ -410,7 +555,7 @@ TEST(Element, MapElement) {
|
||||
"9123456789abcdefa123456789abcdefb123456789abcdef"
|
||||
"c123456789abcdefd123456789abcdefe123456789abcdef"
|
||||
"f123456789abcde");
|
||||
|
||||
|
||||
EXPECT_EQ(255, long_maptag.length()); // check prerequisite
|
||||
el = Element::fromJSON("{ \"" + long_maptag + "\": \"bar\"}");
|
||||
EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
|
||||
@ -689,7 +834,7 @@ TEST(Element, merge) {
|
||||
c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
|
||||
merge(b, a);
|
||||
EXPECT_EQ(*b, *c);
|
||||
|
||||
|
||||
// And some tests with multiple values
|
||||
a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
|
||||
b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
|
||||
|
@ -947,7 +947,7 @@ public:
|
||||
PARTIALMATCH, ///< A superdomain node was found
|
||||
NOTFOUND, ///< Not even any superdomain was found
|
||||
/// \brief Returned by insert() if a node of the name already exists
|
||||
ALREADYEXISTS,
|
||||
ALREADYEXISTS
|
||||
};
|
||||
|
||||
/// \brief Allocate and construct \c DomainTree
|
||||
@ -1080,55 +1080,25 @@ public:
|
||||
/// of it. In that case, node parameter is left intact.
|
||||
//@{
|
||||
|
||||
/// \brief Simple find.
|
||||
/// \brief Simple find
|
||||
///
|
||||
/// Acts as described in the \ref find section.
|
||||
Result find(const isc::dns::Name& name,
|
||||
DomainTreeNode<T>** node) const {
|
||||
DomainTreeNodeChain<T> node_path;
|
||||
const isc::dns::LabelSequence ls(name);
|
||||
return (find<void*>(ls, node, node_path, NULL, NULL));
|
||||
}
|
||||
|
||||
/// \brief Simple find returning immutable node.
|
||||
///
|
||||
/// Acts as described in the \ref find section, but returns immutable node
|
||||
/// pointer.
|
||||
Result find(const isc::dns::Name& name,
|
||||
const DomainTreeNode<T>** node) const {
|
||||
DomainTreeNodeChain<T> node_path;
|
||||
DomainTreeNode<T> *target_node = NULL;
|
||||
const isc::dns::LabelSequence ls(name);
|
||||
Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
|
||||
if (ret != NOTFOUND) {
|
||||
*node = target_node;
|
||||
}
|
||||
Result ret = (find<void*>(ls, node, node_path, NULL, NULL));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/// \brief Simple find, with node_path tracking
|
||||
///
|
||||
/// Acts as described in the \ref find section.
|
||||
Result find(const isc::dns::Name& name, DomainTreeNode<T>** node,
|
||||
DomainTreeNodeChain<T>& node_path) const
|
||||
{
|
||||
const isc::dns::LabelSequence ls(name);
|
||||
return (find<void*>(ls, node, node_path, NULL, NULL));
|
||||
}
|
||||
|
||||
/// \brief Simple find returning immutable node, with node_path tracking
|
||||
///
|
||||
/// Acts as described in the \ref find section, but returns immutable node
|
||||
/// pointer.
|
||||
Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node,
|
||||
DomainTreeNodeChain<T>& node_path) const
|
||||
{
|
||||
DomainTreeNode<T> *target_node = NULL;
|
||||
const isc::dns::LabelSequence ls(name);
|
||||
Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
|
||||
if (ret != NOTFOUND) {
|
||||
*node = target_node;
|
||||
}
|
||||
Result ret = (find<void*>(ls, node, node_path, NULL, NULL));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@ -1143,13 +1113,9 @@ public:
|
||||
bool (*callback)(const DomainTreeNode<T>&, CBARG),
|
||||
CBARG callback_arg) const
|
||||
{
|
||||
DomainTreeNode<T>* target_node = NULL;
|
||||
const isc::dns::LabelSequence ls(name);
|
||||
Result ret = find(ls, &target_node, node_path, callback,
|
||||
Result ret = find(ls, node, node_path, callback,
|
||||
callback_arg);
|
||||
if (ret != NOTFOUND) {
|
||||
*node = target_node;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@ -1229,30 +1195,10 @@ public:
|
||||
/// \c true, it returns immediately with the current node.
|
||||
template <typename CBARG>
|
||||
Result find(const isc::dns::LabelSequence& target_labels_orig,
|
||||
DomainTreeNode<T>** node,
|
||||
DomainTreeNodeChain<T>& node_path,
|
||||
bool (*callback)(const DomainTreeNode<T>&, CBARG),
|
||||
CBARG callback_arg) const;
|
||||
|
||||
/// \brief Simple find returning immutable node.
|
||||
///
|
||||
/// Acts as described in the \ref find section, but returns immutable
|
||||
/// node pointer.
|
||||
template <typename CBARG>
|
||||
Result find(const isc::dns::LabelSequence& target_labels,
|
||||
const DomainTreeNode<T>** node,
|
||||
DomainTreeNodeChain<T>& node_path,
|
||||
bool (*callback)(const DomainTreeNode<T>&, CBARG),
|
||||
CBARG callback_arg) const
|
||||
{
|
||||
DomainTreeNode<T>* target_node = NULL;
|
||||
Result ret = find(target_labels, &target_node, node_path,
|
||||
callback, callback_arg);
|
||||
if (ret != NOTFOUND) {
|
||||
*node = target_node;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
CBARG callback_arg) const;
|
||||
//@}
|
||||
|
||||
/// \brief return the next bigger node in DNSSEC order from a given node
|
||||
@ -1515,7 +1461,7 @@ template <typename T>
|
||||
template <typename CBARG>
|
||||
typename DomainTree<T>::Result
|
||||
DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
|
||||
DomainTreeNode<T>** target,
|
||||
const DomainTreeNode<T>** target,
|
||||
DomainTreeNodeChain<T>& node_path,
|
||||
bool (*callback)(const DomainTreeNode<T>&, CBARG),
|
||||
CBARG callback_arg) const
|
||||
@ -1526,11 +1472,11 @@ DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
|
||||
" and label sequence");
|
||||
}
|
||||
|
||||
DomainTreeNode<T>* node;
|
||||
const DomainTreeNode<T>* node;
|
||||
|
||||
if (!node_path.isEmpty()) {
|
||||
// Get the top node in the node chain
|
||||
node = const_cast<DomainTreeNode<T>*>(node_path.top());
|
||||
node = node_path.top();
|
||||
// Start searching from its down pointer
|
||||
node = node->getDown();
|
||||
} else {
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <dns/nsec3hash.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
#include <dns/masterload.h>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
@ -627,22 +626,20 @@ InMemoryClient::InMemoryClientImpl::load(
|
||||
// node must point to a valid node now
|
||||
assert(node != NULL);
|
||||
|
||||
std::string* tstr = node->setData(new std::string(filename));
|
||||
const std::string* tstr = node->setData(new std::string(filename));
|
||||
delete tstr;
|
||||
|
||||
ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_,
|
||||
zone_name));
|
||||
const ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_,
|
||||
zone_name,
|
||||
holder.release()));
|
||||
if (result.code == result::SUCCESS) {
|
||||
// Only increment the zone count if the zone doesn't already
|
||||
// exist.
|
||||
++zone_count_;
|
||||
}
|
||||
|
||||
ZoneTable::FindResult fr(zone_table_->setZoneData(zone_name,
|
||||
holder.release()));
|
||||
assert(fr.code == result::SUCCESS);
|
||||
if (fr.zone_data != NULL) {
|
||||
ZoneData::destroy(mem_sgmt_, fr.zone_data, rrclass_);
|
||||
// Destroy the old instance of the zone if there was any
|
||||
if (result.zone_data != NULL) {
|
||||
ZoneData::destroy(mem_sgmt_, result.zone_data, rrclass_);
|
||||
}
|
||||
|
||||
return (result.code);
|
||||
@ -732,9 +729,9 @@ InMemoryClient::load(const isc::dns::Name& zone_name,
|
||||
|
||||
const std::string
|
||||
InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
|
||||
FileNameNode* node(NULL);
|
||||
FileNameTree::Result result = impl_->file_name_tree_->find(zone_name,
|
||||
&node);
|
||||
const FileNameNode* node(NULL);
|
||||
const FileNameTree::Result result = impl_->file_name_tree_->find(zone_name,
|
||||
&node);
|
||||
if (result == FileNameTree::EXACTMATCH) {
|
||||
return (*node->getData());
|
||||
} else {
|
||||
@ -742,24 +739,6 @@ InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
|
||||
}
|
||||
}
|
||||
|
||||
result::Result
|
||||
InMemoryClient::add(const isc::dns::Name& zone_name,
|
||||
const ConstRRsetPtr& rrset)
|
||||
{
|
||||
const ZoneTable::FindResult result =
|
||||
impl_->zone_table_->findZone(zone_name);
|
||||
if (result.code != result::SUCCESS) {
|
||||
isc_throw(DataSourceError, "No such zone: " + zone_name.toText());
|
||||
}
|
||||
|
||||
const ConstRRsetPtr sig_rrset =
|
||||
rrset ? rrset->getRRsig() : ConstRRsetPtr();
|
||||
impl_->add(rrset, sig_rrset, zone_name, *result.zone_data);
|
||||
|
||||
// add() doesn't allow duplicate add, so we always return SUCCESS.
|
||||
return (result::SUCCESS);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class MemoryIterator : public ZoneIterator {
|
||||
|
@ -139,35 +139,6 @@ public:
|
||||
/// zone from a file before.
|
||||
const std::string getFileName(const isc::dns::Name& zone_name) const;
|
||||
|
||||
/// \brief Inserts an rrset into the zone.
|
||||
///
|
||||
/// It puts another RRset into the zone.
|
||||
///
|
||||
/// In the current implementation, this method doesn't allow an existing
|
||||
/// RRset to be updated or overridden. So the caller must make sure that
|
||||
/// all RRs of the same type and name must be given in the form of a
|
||||
/// single RRset. The current implementation will also require that
|
||||
/// when an RRSIG is added, the RRset to be covered has already been
|
||||
/// added. These restrictions are probably too strict when this data
|
||||
/// source accepts various forms of input, so they should be revisited
|
||||
/// later.
|
||||
///
|
||||
/// Except for NullRRset and OutOfZone, this method does not guarantee
|
||||
/// strong exception safety (it is currently not needed, if it is needed
|
||||
/// in future, it should be implemented).
|
||||
///
|
||||
/// \throw NullRRset \c rrset is a NULL pointer.
|
||||
/// \throw OutOfZone The owner name of \c rrset is outside of the
|
||||
/// origin of the zone.
|
||||
/// \throw AddError Other general errors.
|
||||
/// \throw Others This method might throw standard allocation exceptions.
|
||||
///
|
||||
/// \param rrset The set to add.
|
||||
/// \return SUCCESS or EXIST (if an rrset for given name and type already
|
||||
/// exists).
|
||||
result::Result add(const isc::dns::Name& zone_name,
|
||||
const isc::dns::ConstRRsetPtr& rrset);
|
||||
|
||||
/// \brief RRset is NULL exception.
|
||||
///
|
||||
/// This is thrown if the provided RRset parameter is NULL.
|
||||
|
@ -428,7 +428,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
|
||||
ZoneFinder::FindOptions options,
|
||||
bool out_of_zone_ok = false)
|
||||
{
|
||||
ZoneNode* node = NULL;
|
||||
const ZoneNode* node = NULL;
|
||||
FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
|
||||
|
||||
const ZoneTree& tree(zone_data.getZoneTree());
|
||||
|
@ -69,17 +69,13 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
|
||||
|
||||
ZoneTable::AddResult
|
||||
ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
|
||||
const Name& zone_name)
|
||||
const Name& zone_name, ZoneData* content)
|
||||
{
|
||||
// Create a new ZoneData instance first. If the specified name already
|
||||
// exists in the table, the new data will soon be destroyed, but we want
|
||||
// to make sure if this allocation fails the tree won't be changed to
|
||||
// provide as strong guarantee as possible. In practice, we generally
|
||||
// expect the caller tries to add a zone only when it's a new one, so
|
||||
// this should be a minor concern.
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder(
|
||||
mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class);
|
||||
|
||||
if (content == NULL) {
|
||||
isc_throw(isc::BadValue, "Zone content must not be NULL");
|
||||
}
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder(mem_sgmt, content,
|
||||
zone_class);
|
||||
// Get the node where we put the zone
|
||||
ZoneTableNode* node(NULL);
|
||||
switch (zones_->insert(mem_sgmt, zone_name, &node)) {
|
||||
@ -94,18 +90,18 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
|
||||
// Can Not Happen
|
||||
assert(node != NULL);
|
||||
|
||||
// Is it empty? We either just created it or it might be nonterminal
|
||||
if (node->isEmpty()) {
|
||||
node->setData(holder.get());
|
||||
return (AddResult(result::SUCCESS, holder.release()));
|
||||
} else { // There's something there already
|
||||
return (AddResult(result::EXIST, node->getData()));
|
||||
// We can release now, setData never throws
|
||||
ZoneData* old = node->setData(holder.release());
|
||||
if (old != NULL) {
|
||||
return (AddResult(result::EXIST, old));
|
||||
} else {
|
||||
return (AddResult(result::SUCCESS, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
ZoneTable::FindResult
|
||||
ZoneTable::findZone(const Name& name) const {
|
||||
ZoneTableNode* node(NULL);
|
||||
const ZoneTableNode* node(NULL);
|
||||
result::Result my_result;
|
||||
|
||||
// Translate the return codes
|
||||
@ -132,20 +128,6 @@ ZoneTable::findZone(const Name& name) const {
|
||||
return (FindResult(my_result, node->getData()));
|
||||
}
|
||||
|
||||
ZoneTable::FindResult
|
||||
ZoneTable::setZoneData(const Name& name, ZoneData* data)
|
||||
{
|
||||
ZoneTableNode* node(NULL);
|
||||
|
||||
ZoneTableTree::Result result(zones_->find(name, &node));
|
||||
|
||||
if (result != ZoneTableTree::EXACTMATCH) {
|
||||
return (FindResult(result::NOTFOUND, NULL));
|
||||
} else {
|
||||
return (FindResult(result::SUCCESS, node->setData(data)));
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace memory
|
||||
} // end of namespace datasrc
|
||||
} // end of namespace isc
|
||||
|
@ -74,23 +74,23 @@ private:
|
||||
typedef DomainTreeNode<ZoneData> ZoneTableNode;
|
||||
|
||||
public:
|
||||
/// \brief Result data of addZone() method.
|
||||
struct AddResult {
|
||||
AddResult(result::Result param_code, ZoneData* param_zone_data) :
|
||||
code(param_code), zone_data(param_zone_data)
|
||||
{}
|
||||
const result::Result code;
|
||||
ZoneData* const zone_data;
|
||||
};
|
||||
/// \brief Result data of addZone() method.
|
||||
struct AddResult {
|
||||
AddResult(result::Result param_code, ZoneData* param_zone_data) :
|
||||
code(param_code), zone_data(param_zone_data)
|
||||
{}
|
||||
const result::Result code;
|
||||
ZoneData* const zone_data;
|
||||
};
|
||||
|
||||
/// \brief Result data of findZone() method.
|
||||
struct FindResult {
|
||||
FindResult(result::Result param_code,
|
||||
ZoneData* param_zone_data) :
|
||||
const ZoneData* param_zone_data) :
|
||||
code(param_code), zone_data(param_zone_data)
|
||||
{}
|
||||
const result::Result code;
|
||||
ZoneData* const zone_data;
|
||||
const ZoneData* const zone_data;
|
||||
};
|
||||
|
||||
private:
|
||||
@ -140,30 +140,29 @@ public:
|
||||
|
||||
/// Add a new zone to the \c ZoneTable.
|
||||
///
|
||||
/// This method creates a new \c ZoneData for the given zone name and
|
||||
/// holds it in the internal table. The newly created zone data will be
|
||||
/// returned via the \c zone_data member of the return value. If the given
|
||||
/// zone name already exists in the table, a new data object won't be
|
||||
/// created; instead, the existing corresponding data will be returned.
|
||||
///
|
||||
/// The zone table keeps the ownership of the created zone data; the
|
||||
/// caller must not try to destroy it directly. (We'll eventually
|
||||
/// add an interface to delete specific zone data from the table).
|
||||
/// This method adds a given zone data to the internal table.
|
||||
///
|
||||
/// \throw std::bad_alloc Internal resource allocation fails.
|
||||
///
|
||||
/// \param mem_sgmt The \c MemorySegment to allocate zone data to be
|
||||
/// created. It must be the same segment that was used to create
|
||||
/// the zone table at the time of create().
|
||||
/// created. It must be the same segment that was used to create
|
||||
/// the zone table at the time of create().
|
||||
/// \param zone_name The name of the zone to be added.
|
||||
/// \param zone_class The RR class of the zone. It must be the RR class
|
||||
/// that is supposed to be associated to the zone table.
|
||||
/// that is supposed to be associated to the zone table.
|
||||
/// \param content This one should hold the zone content (the ZoneData).
|
||||
/// The ownership is passed onto the zone table. Must not be null.
|
||||
/// Must correspond to the name and class and must be allocated from
|
||||
/// mem_sgmt.
|
||||
/// \return \c result::SUCCESS If the zone is successfully
|
||||
/// added to the zone table.
|
||||
/// \return \c result::EXIST The zone table already contains
|
||||
/// zone of the same origin.
|
||||
AddResult addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class,
|
||||
const dns::Name& zone_name);
|
||||
/// added to the zone table.
|
||||
/// \return \c result::EXIST The zone table already contained
|
||||
/// zone of the same origin. The old data is replaced and returned
|
||||
/// inside the result.
|
||||
AddResult addZone(util::MemorySegment& mem_sgmt,
|
||||
dns::RRClass zone_class,
|
||||
const dns::Name& zone_name,
|
||||
ZoneData* content);
|
||||
|
||||
/// Find a zone that best matches the given name in the \c ZoneTable.
|
||||
///
|
||||
@ -185,16 +184,6 @@ public:
|
||||
/// \return A \c FindResult object enclosing the search result (see above).
|
||||
FindResult findZone(const isc::dns::Name& name) const;
|
||||
|
||||
/// Override the ZoneData for a node (zone) in the zone tree.
|
||||
///
|
||||
/// \throw none
|
||||
///
|
||||
/// \param name A domain name for which the zone data is set.
|
||||
/// \param data The new zone data to set.
|
||||
/// \return A \c FindResult object containing the old data if the
|
||||
/// zone was found.
|
||||
FindResult setZoneData(const isc::dns::Name& name, ZoneData* data);
|
||||
|
||||
private:
|
||||
boost::interprocess::offset_ptr<ZoneTableTree> zones_;
|
||||
};
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <dns/nsec3hash.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
#include <dns/masterload.h>
|
||||
|
||||
#include <datasrc/memory_datasrc.h>
|
||||
|
@ -129,7 +129,7 @@ checkConfig(ConstElementPtr config, ElementPtr errors) {
|
||||
result = false;
|
||||
} else {
|
||||
try {
|
||||
RRClass rrc(config->get("class")->stringValue());
|
||||
RRClass(config->get("class")->stringValue());
|
||||
} catch (const isc::Exception& rrce) {
|
||||
addError(errors,
|
||||
"Error parsing class config for memory backend: " +
|
||||
|
@ -256,8 +256,8 @@ TEST_F(DomainTreeTest, subTreeRoot) {
|
||||
|
||||
// "g.h" is not a subtree root
|
||||
EXPECT_EQ(TestDomainTree::EXACTMATCH,
|
||||
dtree_expose_empty_node.find(Name("g.h"), &dtnode));
|
||||
EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
dtree_expose_empty_node.find(Name("g.h"), &cdtnode));
|
||||
EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
|
||||
// fission the node "g.h"
|
||||
EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
|
||||
@ -270,8 +270,8 @@ TEST_F(DomainTreeTest, subTreeRoot) {
|
||||
|
||||
// "g.h" should be a subtree root now.
|
||||
EXPECT_EQ(TestDomainTree::EXACTMATCH,
|
||||
dtree_expose_empty_node.find(Name("g.h"), &dtnode));
|
||||
EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
dtree_expose_empty_node.find(Name("g.h"), &cdtnode));
|
||||
EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
}
|
||||
|
||||
TEST_F(DomainTreeTest, additionalNodeFission) {
|
||||
@ -286,8 +286,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) {
|
||||
|
||||
// "t.0" is not a subtree root
|
||||
EXPECT_EQ(TestDomainTree::EXACTMATCH,
|
||||
dtree_expose_empty_node.find(Name("t.0"), &dtnode));
|
||||
EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
dtree_expose_empty_node.find(Name("t.0"), &cdtnode));
|
||||
EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
|
||||
// fission the node "t.0"
|
||||
EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
|
||||
@ -300,8 +300,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) {
|
||||
|
||||
// "t.0" should be a subtree root now.
|
||||
EXPECT_EQ(TestDomainTree::EXACTMATCH,
|
||||
dtree_expose_empty_node.find(Name("t.0"), &dtnode));
|
||||
EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
dtree_expose_empty_node.find(Name("t.0"), &cdtnode));
|
||||
EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
|
||||
}
|
||||
|
||||
TEST_F(DomainTreeTest, findName) {
|
||||
@ -328,10 +328,10 @@ TEST_F(DomainTreeTest, findName) {
|
||||
EXPECT_EQ(TestDomainTree::PARTIALMATCH,
|
||||
dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode));
|
||||
|
||||
// find dtnode
|
||||
// find cdtnode
|
||||
EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"),
|
||||
&dtnode));
|
||||
EXPECT_EQ(Name("q"), dtnode->getName());
|
||||
&cdtnode));
|
||||
EXPECT_EQ(Name("q"), cdtnode->getName());
|
||||
}
|
||||
|
||||
TEST_F(DomainTreeTest, findError) {
|
||||
@ -411,11 +411,12 @@ performCallbackTest(TestDomainTree& dtree,
|
||||
Name("example"),
|
||||
&parentdtnode));
|
||||
// the child/parent nodes shouldn't "inherit" the callback flag.
|
||||
// "dtnode" may be invalid due to the insertion, so we need to re-find
|
||||
// it.
|
||||
// "dtnode" should still validly point to "callback.example", but we
|
||||
// explicitly confirm it.
|
||||
EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"),
|
||||
&dtnode));
|
||||
EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
|
||||
&cdtnode));
|
||||
ASSERT_EQ(dtnode, cdtnode);
|
||||
EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
|
||||
EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
|
||||
EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <dns/nsec3hash.h>
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
#include <dns/rrttl.h>
|
||||
#include <dns/masterload.h>
|
||||
|
||||
@ -543,29 +542,6 @@ TEST_F(MemoryClientTest, loadRRSIGs) {
|
||||
EXPECT_EQ(1, client_->getZoneCount());
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
|
||||
RRsetPtr rrset(new RRset(Name("example.org"),
|
||||
RRClass::IN(), RRType::A(), RRTTL(3600)));
|
||||
rrset->addRdata(in::A("192.0.2.1"));
|
||||
rrset->addRdata(in::A("192.0.2.2"));
|
||||
|
||||
RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
|
||||
RRType::RRSIG(), RRTTL(300)));
|
||||
rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
|
||||
"12345 example.org. FAKEFAKEFAKE"));
|
||||
rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
|
||||
"54321 example.org. FAKEFAKEFAKEFAKE"));
|
||||
rrset->addRRsig(rrsig);
|
||||
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset),
|
||||
InMemoryClient::AddError);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, getZoneCount) {
|
||||
EXPECT_EQ(0, client_->getZoneCount());
|
||||
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
|
||||
@ -655,75 +631,6 @@ TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
|
||||
EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
|
||||
// The zone "example.org" doesn't exist, so we can't add an RRset to
|
||||
// it.
|
||||
RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
|
||||
RRTTL(300)));
|
||||
rrset_a->addRdata(rdata::in::A("192.0.2.1"));
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addOutOfZoneThrows) {
|
||||
// Out of zone names should throw.
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-empty.zone");
|
||||
|
||||
RRsetPtr rrset_a(new RRset(Name("a.example.com"),
|
||||
RRClass::IN(), RRType::A(), RRTTL(300)));
|
||||
rrset_a->addRdata(rdata::in::A("192.0.2.1"));
|
||||
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
|
||||
OutOfZone);
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addNullRRsetThrows) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
|
||||
EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
|
||||
InMemoryClient::NullRRset);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
|
||||
RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
|
||||
RRTTL(300)));
|
||||
EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
|
||||
InMemoryClient::AddError);
|
||||
|
||||
// Teardown checks for memory segment leaks
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, add) {
|
||||
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
|
||||
|
||||
// Add another RRset
|
||||
RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
|
||||
RRTTL(300)));
|
||||
rrset_a->addRdata(rdata::in::A("192.0.2.1"));
|
||||
client_->add(Name("example.org"), rrset_a);
|
||||
|
||||
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
|
||||
|
||||
// First we have the SOA
|
||||
ConstRRsetPtr rrset(iterator->getNextRRset());
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::A(), rrset->getType());
|
||||
|
||||
rrset = iterator->getNextRRset();
|
||||
EXPECT_TRUE(rrset);
|
||||
EXPECT_EQ(RRType::SOA(), rrset->getType());
|
||||
|
||||
// There's nothing else in this zone
|
||||
EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
|
||||
}
|
||||
|
||||
TEST_F(MemoryClientTest, findZoneData) {
|
||||
client_->load(Name("example.org"),
|
||||
TEST_DATA_DIR "/example.org-rrsigs.zone");
|
||||
@ -774,4 +681,16 @@ TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
|
||||
EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
|
||||
isc::NotImplemented);
|
||||
}
|
||||
|
||||
// TODO (upon merge of #2268): Re-add (and modify not to need
|
||||
// InMemoryClient::add) the tests removed in
|
||||
// 7a628baa1a158b5837d6f383e10b30542d2ac59b. Maybe some of them
|
||||
// are really not needed.
|
||||
//
|
||||
// * MemoryClientTest::loadRRSIGsRdataMixedCoveredTypes
|
||||
// * MemoryClientTest::addRRsetToNonExistentZoneThrows
|
||||
// * MemoryClientTest::addOutOfZoneThrows
|
||||
// * MemoryClientTest::addNullRRsetThrows
|
||||
// * MemoryClientTest::addEmptyRRsetThrows
|
||||
// * MemoryClientTest::add
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ void
|
||||
checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
|
||||
const RdataSet* expected_set)
|
||||
{
|
||||
ZoneNode* node = NULL;
|
||||
const ZoneNode* node = NULL;
|
||||
tree.find(name, &node);
|
||||
ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
|
||||
EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <datasrc/result.h>
|
||||
#include <datasrc/memory/zone_data.h>
|
||||
#include <datasrc/memory/zone_table.h>
|
||||
#include <datasrc/memory/segment_object_holder.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@ -30,6 +31,7 @@
|
||||
using namespace isc::dns;
|
||||
using namespace isc::datasrc;
|
||||
using namespace isc::datasrc::memory;
|
||||
using namespace isc::datasrc::memory::detail;
|
||||
|
||||
namespace {
|
||||
// Memory segment specified for tests. It normally behaves like a "local"
|
||||
@ -87,46 +89,89 @@ TEST_F(ZoneTableTest, create) {
|
||||
}
|
||||
|
||||
TEST_F(ZoneTableTest, addZone) {
|
||||
// It doesn't accept empty (NULL) zones
|
||||
EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, zname1, NULL),
|
||||
isc::BadValue);
|
||||
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder1(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
|
||||
const ZoneData* data1(holder1.get());
|
||||
// Normal successful case.
|
||||
const ZoneTable::AddResult result1 =
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname1);
|
||||
const ZoneTable::AddResult result1(zone_table->addZone(mem_sgmt_, zclass_,
|
||||
zname1,
|
||||
holder1.release()));
|
||||
EXPECT_EQ(result::SUCCESS, result1.code);
|
||||
EXPECT_EQ(static_cast<const ZoneData*>(NULL), result1.zone_data);
|
||||
// It got released by it
|
||||
EXPECT_EQ(static_cast<const ZoneData*>(NULL), holder1.get());
|
||||
|
||||
// Duplicate add doesn't replace the existing data.
|
||||
EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
|
||||
zname1).code);
|
||||
EXPECT_EQ(result1.zone_data,
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder2(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
|
||||
const ZoneTable::AddResult result2(zone_table->addZone(mem_sgmt_, zclass_,
|
||||
zname1,
|
||||
holder2.release()));
|
||||
EXPECT_EQ(result::EXIST, result2.code);
|
||||
// The old one gets out
|
||||
EXPECT_EQ(data1, result2.zone_data);
|
||||
// It releases this one even when we replace the old zone
|
||||
EXPECT_EQ(static_cast<const ZoneData*>(NULL), holder2.get());
|
||||
// We need to release the old one manually
|
||||
ZoneData::destroy(mem_sgmt_, result2.zone_data, zclass_);
|
||||
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder3(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")),
|
||||
zclass_);
|
||||
// names are compared in a case insensitive manner.
|
||||
EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
|
||||
Name("EXAMPLE.COM")).code);
|
||||
const ZoneTable::AddResult result3(zone_table->addZone(mem_sgmt_, zclass_,
|
||||
Name("EXAMPLE.COM"),
|
||||
holder3.release()));
|
||||
EXPECT_EQ(result::EXIST, result3.code);
|
||||
ZoneData::destroy(mem_sgmt_, result3.zone_data, zclass_);
|
||||
// Add some more different ones. Should just succeed.
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder4(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
|
||||
EXPECT_EQ(result::SUCCESS,
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname2,
|
||||
holder4.release()).code);
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder5(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
|
||||
EXPECT_EQ(result::SUCCESS,
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname3,
|
||||
holder5.release()).code);
|
||||
|
||||
// Have the memory segment throw an exception in extending the internal
|
||||
// tree. It still shouldn't cause memory leak (which would be detected
|
||||
// in TearDown()).
|
||||
mem_sgmt_.setThrowCount(2);
|
||||
EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder6(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_);
|
||||
mem_sgmt_.setThrowCount(1);
|
||||
EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"),
|
||||
holder6.release()),
|
||||
std::bad_alloc);
|
||||
}
|
||||
|
||||
TEST_F(ZoneTableTest, findZone) {
|
||||
const ZoneTable::AddResult add_result1 =
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname1);
|
||||
EXPECT_EQ(result::SUCCESS, add_result1.code);
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder1(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
|
||||
ZoneData* zone_data = holder1.get();
|
||||
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1,
|
||||
holder1.release()).code);
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder2(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
|
||||
EXPECT_EQ(result::SUCCESS,
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname2,
|
||||
holder2.release()).code);
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder3(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
|
||||
EXPECT_EQ(result::SUCCESS,
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
|
||||
zone_table->addZone(mem_sgmt_, zclass_, zname3,
|
||||
holder3.release()).code);
|
||||
|
||||
const ZoneTable::FindResult find_result1 =
|
||||
zone_table->findZone(Name("example.com"));
|
||||
EXPECT_EQ(result::SUCCESS, find_result1.code);
|
||||
EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
|
||||
EXPECT_EQ(zone_data, find_result1.zone_data);
|
||||
|
||||
EXPECT_EQ(result::NOTFOUND,
|
||||
zone_table->findZone(Name("example.org")).code);
|
||||
@ -137,14 +182,17 @@ TEST_F(ZoneTableTest, findZone) {
|
||||
// and the code should be PARTIALMATCH.
|
||||
EXPECT_EQ(result::PARTIALMATCH,
|
||||
zone_table->findZone(Name("www.example.com")).code);
|
||||
EXPECT_EQ(add_result1.zone_data,
|
||||
EXPECT_EQ(zone_data,
|
||||
zone_table->findZone(Name("www.example.com")).zone_data);
|
||||
|
||||
// make sure the partial match is indeed the longest match by adding
|
||||
// a zone with a shorter origin and query again.
|
||||
SegmentObjectHolder<ZoneData, RRClass> holder4(
|
||||
mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_);
|
||||
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
|
||||
Name("com")).code);
|
||||
EXPECT_EQ(add_result1.zone_data,
|
||||
Name("com"),
|
||||
holder4.release()).code);
|
||||
EXPECT_EQ(zone_data,
|
||||
zone_table->findZone(Name("www.example.com")).zone_data);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
#include <dns/rrttl.h>
|
||||
#include <dns/masterload.h>
|
||||
|
||||
|
@ -16,6 +16,7 @@ CLEANFILES = *.gcno *.gcda
|
||||
lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la
|
||||
libb10_dhcp___la_SOURCES =
|
||||
libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
|
||||
libb10_dhcp___la_SOURCES += lease_mgr.cc lease_mgr.h
|
||||
libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
|
||||
libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
|
||||
libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
|
||||
@ -28,6 +29,7 @@ libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
|
||||
libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
|
||||
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
|
||||
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
|
||||
libb10_dhcp___la_SOURCES += duid.cc duid.h
|
||||
|
||||
libb10_dhcpsrv_la_SOURCES = cfgmgr.cc cfgmgr.h
|
||||
libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
|
||||
|
@ -13,17 +13,39 @@
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <string.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <dhcp/addr_utilities.h>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
namespace {
|
||||
|
||||
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
/// @brief mask used for first/last address calculation in a IPv4 prefix
|
||||
///
|
||||
/// Using a static mask is faster than calculating it dynamically every time.
|
||||
const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
|
||||
0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
|
||||
0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
|
||||
0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
|
||||
0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
|
||||
0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
|
||||
0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
|
||||
0x0000000f, 0x00000007, 0x00000003, 0x00000001,
|
||||
0x00000000 };
|
||||
|
||||
/// @brief mask used for first/last address calculation in a IPv6 prefix
|
||||
const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
|
||||
|
||||
/// @brief calculates the first IPv6 address in a IPv6 prefix
|
||||
///
|
||||
/// Note: This is a private function. Do not use it directly.
|
||||
/// Please use firstAddrInPrefix() instead.
|
||||
///
|
||||
/// @param prefix IPv6 prefix
|
||||
/// @param len prefix length
|
||||
isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
|
||||
const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
|
||||
uint8_t packed[V6ADDRESS_LEN];
|
||||
|
||||
// First we copy the whole address as 16 bytes.
|
||||
@ -36,7 +58,7 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
|
||||
|
||||
// Get the appropriate mask. It has relevant bits (those that should
|
||||
// stay) set and irrelevant (those that should be wiped) cleared.
|
||||
uint8_t mask = bitMask[len % 8];
|
||||
uint8_t mask = bitMask6[len % 8];
|
||||
|
||||
// Let's leave only whatever the mask says should not be cleared.
|
||||
packed[len / 8] = packed[len / 8] & mask;
|
||||
@ -55,10 +77,50 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
|
||||
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
/// @brief calculates the first IPv4 address in a IPv4 prefix
|
||||
///
|
||||
/// Note: This is a private function. Do not use it directly.
|
||||
/// Please use firstAddrInPrefix() instead.
|
||||
///
|
||||
/// @param prefix IPv4 prefix
|
||||
/// @param len netmask length (0-32)
|
||||
isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
uint32_t addr = prefix;
|
||||
if (len > 32) {
|
||||
isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
|
||||
}
|
||||
|
||||
return (IOAddress(addr & (~bitMask4[len])));
|
||||
}
|
||||
|
||||
/// @brief calculates the last IPv4 address in a IPv4 prefix
|
||||
///
|
||||
/// Note: This is a private function. Do not use it directly.
|
||||
/// Please use firstAddrInPrefix() instead.
|
||||
///
|
||||
/// @param prefix IPv4 prefix that we calculate first address for
|
||||
/// @param len netmask length (0-32)
|
||||
isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
uint32_t addr = prefix;
|
||||
if (len>32) {
|
||||
isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
|
||||
}
|
||||
|
||||
return (IOAddress(addr | bitMask4[len]));
|
||||
}
|
||||
|
||||
/// @brief calculates the last IPv6 address in a IPv6 prefix
|
||||
///
|
||||
/// Note: This is a private function. Do not use it directly.
|
||||
/// Please use lastAddrInPrefix() instead.
|
||||
///
|
||||
/// @param prefix IPv6 prefix that we calculate first address for
|
||||
/// @param len netmask length (0-128)
|
||||
isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
|
||||
const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
|
||||
uint8_t packed[V6ADDRESS_LEN];
|
||||
|
||||
// First we copy the whole address as 16 bytes.
|
||||
@ -70,10 +132,10 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix
|
||||
if (len % 8 != 0) {
|
||||
// Get the appropriate mask. It has relevant bits (those that should
|
||||
// stay) set and irrelevant (those that should be set to 1) cleared.
|
||||
uint8_t mask = bitMask[len % 8];
|
||||
uint8_t mask = bitMask6[len % 8];
|
||||
|
||||
// Let's set those irrelevant bits with 1. It would be perhaps
|
||||
// easier to not use negation here and invert bitMask content. However,
|
||||
// easier to not use negation here and invert bitMask6 content. However,
|
||||
// with this approach, we can use the same mask in first and last
|
||||
// address calculations.
|
||||
packed[len / 8] = packed[len / 8] | ~mask;
|
||||
@ -92,5 +154,28 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix
|
||||
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
|
||||
}
|
||||
|
||||
}; // end of anonymous namespace
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
if (prefix.getFamily() == AF_INET) {
|
||||
return firstAddrInPrefix4(prefix, len);
|
||||
} else {
|
||||
return firstAddrInPrefix6(prefix, len);
|
||||
}
|
||||
}
|
||||
|
||||
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t len) {
|
||||
if (prefix.getFamily() == AF_INET) {
|
||||
return lastAddrInPrefix4(prefix, len);
|
||||
} else {
|
||||
return lastAddrInPrefix6(prefix, len);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
@ -68,6 +68,39 @@ void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
|
||||
subnets6_.push_back(subnet);
|
||||
}
|
||||
|
||||
Subnet4Ptr
|
||||
CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) {
|
||||
|
||||
// If there's only one subnet configured, let's just use it
|
||||
// The idea is to keep small deployments easy. In a small network - one
|
||||
// router that also runs DHCPv6 server. Users specifies a single pool and
|
||||
// expects it to just work. Without this, the server would complain that it
|
||||
// doesn't have IP address on its interfaces that matches that
|
||||
// configuration. Such requirement makes sense in IPv4, but not in IPv6.
|
||||
// The server does not need to have a global address (using just link-local
|
||||
// is ok for DHCPv6 server) from the pool it serves.
|
||||
if (subnets4_.size() == 1) {
|
||||
return (subnets4_[0]);
|
||||
}
|
||||
|
||||
// If there is more than one, we need to choose the proper one
|
||||
for (Subnet4Collection::iterator subnet = subnets4_.begin();
|
||||
subnet != subnets4_.end(); ++subnet) {
|
||||
if ((*subnet)->inRange(hint)) {
|
||||
return (*subnet);
|
||||
}
|
||||
}
|
||||
|
||||
// sorry, we don't support that subnet
|
||||
return (Subnet4Ptr());
|
||||
}
|
||||
|
||||
void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
|
||||
/// @todo: Check that this new subnet does not cross boundaries of any
|
||||
/// other already defined subnet.
|
||||
subnets4_.push_back(subnet);
|
||||
}
|
||||
|
||||
CfgMgr::CfgMgr() {
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ public:
|
||||
/// accessing it.
|
||||
static CfgMgr& instance();
|
||||
|
||||
/// @brief get subnet by address
|
||||
/// @brief get IPv6 subnet by address
|
||||
///
|
||||
/// Finds a matching subnet, based on an address. This can be used
|
||||
/// in two cases: when trying to find an appropriate lease based on
|
||||
@ -83,7 +83,7 @@ public:
|
||||
/// @param hint an address that belongs to a searched subnet
|
||||
Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
|
||||
|
||||
/// @brief get subnet by interface-id
|
||||
/// @brief get IPv6 subnet by interface-id
|
||||
///
|
||||
/// Another possibility to find a subnet is based on interface-id.
|
||||
///
|
||||
@ -91,13 +91,44 @@ public:
|
||||
/// @todo This method is not currently supported.
|
||||
Subnet6Ptr getSubnet6(OptionPtr interface_id);
|
||||
|
||||
/// @brief adds a subnet6
|
||||
/// @brief adds an IPv6 subnet
|
||||
void addSubnet6(const Subnet6Ptr& subnet);
|
||||
|
||||
/// @todo: Add subnet6 removal routines. Currently it is not possible
|
||||
/// to remove subnets. The only case where subnet6 removal would be
|
||||
/// needed is a dynamic server reconfiguration - a use case that is not
|
||||
/// planned to be supported any time soon.
|
||||
|
||||
/// @brief removes all subnets
|
||||
///
|
||||
/// This method removes all existing subnets. It is used during
|
||||
/// reconfiguration - old configuration is wiped and new definitions
|
||||
/// are used to recreate subnets.
|
||||
///
|
||||
/// @todo Implement more intelligent approach. Note that comparison
|
||||
/// between old and new configuration is tricky. For example: is
|
||||
/// 2000::/64 and 2000::/48 the same subnet or is it something
|
||||
/// completely new?
|
||||
void deleteSubnets6() {
|
||||
subnets6_.clear();
|
||||
}
|
||||
|
||||
/// @brief get IPv4 subnet by address
|
||||
///
|
||||
/// Finds a matching subnet, based on an address. This can be used
|
||||
/// in two cases: when trying to find an appropriate lease based on
|
||||
/// a) relay link address (that must be the address that is on link)
|
||||
/// b) our global address on the interface the message was received on
|
||||
/// (for directly connected clients)
|
||||
///
|
||||
/// @param hint an address that belongs to a searched subnet
|
||||
Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint);
|
||||
|
||||
/// @brief adds a subnet4
|
||||
void addSubnet4(const Subnet4Ptr& subnet);
|
||||
|
||||
/// @brief removes all IPv4 subnets
|
||||
void removeSubnets4();
|
||||
protected:
|
||||
|
||||
/// @brief Protected constructor.
|
||||
@ -111,13 +142,21 @@ protected:
|
||||
/// @brief virtual desctructor
|
||||
virtual ~CfgMgr();
|
||||
|
||||
/// @brief a container for Subnet6
|
||||
/// @brief a container for IPv6 subnets.
|
||||
///
|
||||
/// That is a simple vector of pointers. It does not make much sense to
|
||||
/// optimize access time (e.g. using a map), because typical search
|
||||
/// pattern will use calling inRange() method on each subnet until
|
||||
/// a match is found.
|
||||
Subnet6Collection subnets6_;
|
||||
|
||||
/// @brief a container for IPv4 subnets.
|
||||
///
|
||||
/// That is a simple vector of pointers. It does not make much sense to
|
||||
/// optimize access time (e.g. using a map), because typical search
|
||||
/// pattern will use calling inRange() method on each subnet until
|
||||
/// a match is found.
|
||||
Subnet4Collection subnets4_;
|
||||
};
|
||||
|
||||
} // namespace isc::dhcp
|
||||
|
90
src/lib/dhcp/duid.cc
Normal file
90
src/lib/dhcp/duid.cc
Normal file
@ -0,0 +1,90 @@
|
||||
// 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 <vector>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <stdint.h>
|
||||
#include <util/io_utilities.h>
|
||||
#include <dhcp/duid.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
DUID::DUID(const std::vector<uint8_t>& duid) {
|
||||
if (duid.size() > MAX_DUID_LEN) {
|
||||
isc_throw(OutOfRange, "DUID too large");
|
||||
} else {
|
||||
duid_ = duid;
|
||||
}
|
||||
}
|
||||
|
||||
DUID::DUID(const uint8_t * data, size_t len) {
|
||||
if (len > MAX_DUID_LEN) {
|
||||
isc_throw(OutOfRange, "DUID too large");
|
||||
}
|
||||
|
||||
duid_ = std::vector<uint8_t>(data, data + len);
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> DUID::getDuid() const {
|
||||
return (duid_);
|
||||
}
|
||||
|
||||
DUID::DUIDType DUID::getType() const {
|
||||
if (duid_.size() < 2) {
|
||||
return (DUID_UNKNOWN);
|
||||
}
|
||||
uint16_t type = (duid_[0] << 8) + duid_[1];
|
||||
if (type < DUID_MAX) {
|
||||
return (static_cast<DUID::DUIDType>(type));
|
||||
} else {
|
||||
return (DUID_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
bool DUID::operator == (const DUID& other) const {
|
||||
return (this->duid_ == other.duid_);
|
||||
}
|
||||
|
||||
bool DUID::operator != (const DUID& other) const {
|
||||
return (this->duid_ != other.duid_);
|
||||
}
|
||||
|
||||
/// constructor based on vector<uint8_t>
|
||||
ClientId::ClientId(const std::vector<uint8_t>& clientid)
|
||||
:DUID(clientid) {
|
||||
}
|
||||
|
||||
/// constructor based on C-style data
|
||||
ClientId::ClientId(const uint8_t *clientid, size_t len)
|
||||
:DUID(clientid, len) {
|
||||
}
|
||||
|
||||
/// @brief returns a copy of client-id data
|
||||
const std::vector<uint8_t> ClientId::getClientId() const {
|
||||
return (duid_);
|
||||
}
|
||||
|
||||
// compares two client-ids
|
||||
bool ClientId::operator == (const ClientId& other) const {
|
||||
return (this->duid_ == other.duid_);
|
||||
}
|
||||
|
||||
// compares two client-ids
|
||||
bool ClientId::operator != (const ClientId& other) const {
|
||||
return (this->duid_ != other.duid_);
|
||||
}
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
98
src/lib/dhcp/duid.h
Normal file
98
src/lib/dhcp/duid.h
Normal file
@ -0,0 +1,98 @@
|
||||
// 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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <asiolink/io_address.h>
|
||||
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief Holds DUID (DHCPv6 Unique Identifier)
|
||||
///
|
||||
/// This class holds DUID, that is used in client-id, server-id and
|
||||
/// several other options. It is used to identify DHCPv6 entity.
|
||||
class DUID {
|
||||
public:
|
||||
/// @brief maximum duid size
|
||||
/// As defined in RFC3315, section 9.1
|
||||
static const size_t MAX_DUID_LEN = 128;
|
||||
|
||||
/// @brief specifies DUID type
|
||||
typedef enum {
|
||||
DUID_UNKNOWN = 0, ///< invalid/unknown type
|
||||
DUID_LLT = 1, ///< link-layer + time, see RFC3315, section 9.2
|
||||
DUID_EN = 2, ///< enterprise-id, see RFC3315, section 9.3
|
||||
DUID_LL = 3, ///< link-layer, see RFC3315, section 9.4
|
||||
DUID_UUID = 4, ///< UUID, see RFC6355
|
||||
DUID_MAX ///< not a real type, just maximum defined value + 1
|
||||
} DUIDType;
|
||||
|
||||
/// @brief creates a DUID
|
||||
DUID(const std::vector<uint8_t>& duid);
|
||||
|
||||
/// @brief creates a DUID
|
||||
DUID(const uint8_t *duid, size_t len);
|
||||
|
||||
/// @brief returns a const reference to the actual DUID value
|
||||
///
|
||||
/// Note: For safety reasons, this method returns a copy of data as
|
||||
/// otherwise the reference would be only valid as long as the object that
|
||||
/// returned it. In any case, this method should be used only sporadically.
|
||||
/// If there are frequent uses, we must implement some other method
|
||||
/// (e.g. storeSelf()) that will avoid data copying.
|
||||
const std::vector<uint8_t> getDuid() const;
|
||||
|
||||
/// @brief returns DUID type
|
||||
DUIDType getType() const;
|
||||
|
||||
// compares two DUIDs
|
||||
bool operator == (const DUID& other) const;
|
||||
|
||||
// compares two DUIDs
|
||||
bool operator != (const DUID& other) const;
|
||||
|
||||
protected:
|
||||
/// the actual content of the DUID
|
||||
std::vector<uint8_t> duid_;
|
||||
};
|
||||
|
||||
/// @brief Holds Client identifier or client IPv4 address
|
||||
///
|
||||
/// This class is intended to be a generic IPv4 client identifier. It can hold
|
||||
/// a client-id
|
||||
class ClientId : DUID {
|
||||
public:
|
||||
|
||||
/// constructor based on vector<uint8_t>
|
||||
ClientId(const std::vector<uint8_t>& clientid);
|
||||
|
||||
/// constructor based on C-style data
|
||||
ClientId(const uint8_t *clientid, size_t len);
|
||||
|
||||
/// @brief returns reference to the client-id data
|
||||
///
|
||||
const std::vector<uint8_t> getClientId() const;
|
||||
|
||||
// compares two client-ids
|
||||
bool operator == (const ClientId& other) const;
|
||||
|
||||
// compares two client-ids
|
||||
bool operator != (const ClientId& other) const;
|
||||
};
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
68
src/lib/dhcp/lease_mgr.cc
Normal file
68
src/lib/dhcp/lease_mgr.cc
Normal file
@ -0,0 +1,68 @@
|
||||
// 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 <sstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "lease_mgr.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace isc::dhcp;
|
||||
|
||||
LeaseMgr::LeaseMgr(const std::string& dbconfig) {
|
||||
|
||||
if (dbconfig.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector<string> tokens;
|
||||
|
||||
// we need to pass a string to is_any_of, not just char *. Otherwise there
|
||||
// are cryptic warnings on Debian6 running g++ 4.4 in /usr/include/c++/4.4
|
||||
// /bits/stl_algo.h:2178 "array subscript is above array bounds"
|
||||
boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") ));
|
||||
BOOST_FOREACH(std::string token, tokens) {
|
||||
size_t pos = token.find("=");
|
||||
if (pos != string::npos) {
|
||||
string name = token.substr(0, pos);
|
||||
string value = token.substr(pos + 1);
|
||||
parameters_.insert(pair<string,string>(name, value));
|
||||
} else {
|
||||
isc_throw(InvalidParameter, "Cannot parse " << token
|
||||
<< ", expected format is name=value");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::string LeaseMgr::getParameter(const std::string& name) const {
|
||||
std::map<std::string, std::string>::const_iterator param
|
||||
= parameters_.find(name);
|
||||
if (param == parameters_.end()) {
|
||||
isc_throw(BadValue, "Parameter not found");
|
||||
}
|
||||
return (param->second);
|
||||
}
|
||||
|
||||
LeaseMgr::~LeaseMgr() {
|
||||
}
|
480
src/lib/dhcp/lease_mgr.h
Normal file
480
src/lib/dhcp/lease_mgr.h
Normal file
@ -0,0 +1,480 @@
|
||||
// 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 <string>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/duid.h>
|
||||
|
||||
/// @file dhcp/lease_mgr.h
|
||||
/// @brief An abstract API for lease database
|
||||
///
|
||||
/// This file contains declarations of Lease4, Lease6 and LeaseMgr classes.
|
||||
/// They are essential components of the interface to any database backend.
|
||||
/// Each concrete database backend (e.g. MySQL) will define a class derived
|
||||
/// from LeaseMgr class.
|
||||
///
|
||||
/// Failover considerations:
|
||||
/// There are no intermediate plans to implement DHCPv4 failover
|
||||
/// (draft-ietf-dhc-failover-12.txt). Currently (Oct. 2012) the DHCPv6 failover
|
||||
/// is being defined in DHC WG in IETF (draft-ietf-dhcpv6-failover-requirements,
|
||||
/// draft-ietf-dhcpv6-dailover-design), but the work is not advanced enough
|
||||
/// for implementation plans yet. v4 failover requires additional parameters
|
||||
/// to be kept with a lease. It is likely that v6 failover will require similar
|
||||
/// fields. Such implementation will require database schema extension.
|
||||
/// We have designed a way to expand/upgrade schemas during upgrades: a database
|
||||
/// schema is versioned and sanity checks about required version will be done
|
||||
/// upon start and/or upgrade. With this mechanism in place, we can add new
|
||||
/// fields to the database. In particular we can use that capability to
|
||||
/// introduce failover related fields.
|
||||
///
|
||||
/// However, there is another approach that can be reliably used to provide
|
||||
/// failover, even without the actual failover protocol implemented. As the
|
||||
/// first backend will use MySQL, we will be able to use Multi-Master capability
|
||||
/// offered by MySQL and use two separatate Kea instances connecting to the
|
||||
/// same database.
|
||||
///
|
||||
/// Nevertheless, we hope to have failover protocol eventually implemented in
|
||||
/// the Kea.
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief specifies unique subnet identifier
|
||||
/// @todo: Move this to subnet.h once ticket #2237 is merged
|
||||
typedef uint32_t SubnetID;
|
||||
|
||||
/// @brief Structure that holds a lease for IPv4 address
|
||||
///
|
||||
/// For performance reasons it is a simple structure, not a class. If we chose
|
||||
/// make it a class, all fields would have to made private and getters/setters
|
||||
/// would be required. As this is a critical part of the code that will be used
|
||||
/// extensively, direct access is warranted.
|
||||
struct Lease4 {
|
||||
/// IPv4 address
|
||||
isc::asiolink::IOAddress addr_;
|
||||
|
||||
/// @brief Address extension
|
||||
///
|
||||
/// It is envisaged that in some cases IPv4 address will be accompanied with some
|
||||
/// additional data. One example of such use are Address + Port solutions (or
|
||||
/// Port-restricted Addresses), where several clients may get the same address, but
|
||||
/// different port ranges. This feature is not expected to be widely used.
|
||||
/// Under normal circumstances, the value should be 0.
|
||||
uint32_t ext_;
|
||||
|
||||
/// @brief hardware address
|
||||
std::vector<uint8_t> hwaddr_;
|
||||
|
||||
/// @brief client identifier
|
||||
boost::shared_ptr<ClientId> client_id_;
|
||||
|
||||
/// @brief renewal timer
|
||||
///
|
||||
/// Specifies renewal time. Although technically it is a property of IA container,
|
||||
/// not the address itself, since our data model does not define separate IA
|
||||
/// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
|
||||
/// for the same IA, each must have consistent T1 and T2 values. Specified in
|
||||
/// seconds since cltt.
|
||||
uint32_t t1_;
|
||||
|
||||
/// @brief rebinding timer
|
||||
///
|
||||
/// Specifies rebinding time. Although technically it is a property of IA container,
|
||||
/// not the address itself, since our data model does not define separate IA
|
||||
/// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
|
||||
/// for the same IA, each must have consistent T1 and T2 values. Specified in
|
||||
/// seconds since cltt.
|
||||
uint32_t t2_;
|
||||
|
||||
/// @brief valid lifetime
|
||||
///
|
||||
/// Expressed as number of seconds since cltt
|
||||
uint32_t valid_lft_;
|
||||
|
||||
/// @brief client last transmission time
|
||||
///
|
||||
/// Specifies a timestamp, when last transmission from a client was received.
|
||||
time_t cltt_;
|
||||
|
||||
/// @brief Subnet identifier
|
||||
///
|
||||
/// Specifies subnet-id of the subnet that the lease belongs to
|
||||
SubnetID subnet_id_;
|
||||
|
||||
/// @brief Is this a fixed lease?
|
||||
///
|
||||
/// Fixed leases are kept after they are released/expired.
|
||||
bool fixed_;
|
||||
|
||||
/// @brief client hostname
|
||||
///
|
||||
/// This field may be empty
|
||||
std::string hostname_;
|
||||
|
||||
/// @brief did we update AAAA record for this lease?
|
||||
bool fqdn_fwd_;
|
||||
|
||||
/// @brief did we update PTR record for this lease?
|
||||
bool fqdn_rev_;
|
||||
|
||||
/// @brief Lease comments.
|
||||
///
|
||||
/// Currently not used. It may be used for keeping comments made by the
|
||||
/// system administrator.
|
||||
std::string comments_;
|
||||
|
||||
/// @todo: Add DHCPv4 failover related fields here
|
||||
};
|
||||
|
||||
/// @brief Pointer to a Lease4 structure.
|
||||
typedef boost::shared_ptr<Lease4> Lease4Ptr;
|
||||
|
||||
/// @brief A collection of IPv4 leases.
|
||||
typedef std::vector< boost::shared_ptr<Lease4Ptr> > Lease4Collection;
|
||||
|
||||
/// @brief Structure that holds a lease for IPv6 address and/or prefix
|
||||
///
|
||||
/// For performance reasons it is a simple structure, not a class. Had we chose to
|
||||
/// make it a class, all fields would have to be made private and getters/setters
|
||||
/// would be required. As this is a critical part of the code that will be used
|
||||
/// extensively, direct access rather than through getters/setters is warranted.
|
||||
struct Lease6 {
|
||||
typedef enum {
|
||||
LEASE_IA_NA, /// the lease contains non-temporary IPv6 address
|
||||
LEASE_IA_TA, /// the lease contains temporary IPv6 address
|
||||
LEASE_IA_PD /// the lease contains IPv6 prefix (for prefix delegation)
|
||||
} LeaseType;
|
||||
|
||||
/// @brief specifies lease type (normal addr, temporary addr, prefix)
|
||||
LeaseType type_;
|
||||
|
||||
/// IPv6 address
|
||||
isc::asiolink::IOAddress addr_;
|
||||
|
||||
/// IPv6 prefix length (used only for PD)
|
||||
uint8_t prefixlen_;
|
||||
|
||||
/// @brief IAID
|
||||
///
|
||||
/// Identity Association IDentifier. DHCPv6 stores all addresses and prefixes
|
||||
/// in IA containers (IA_NA, IA_TA, IA_PD). Most containers may appear more
|
||||
/// than once in a message. To differentiate between them, IAID field is present
|
||||
uint32_t iaid_;
|
||||
|
||||
/// @brief hardware address
|
||||
///
|
||||
/// This field is not really used and is optional at best. The concept of identifying
|
||||
/// clients by their hardware address was replaced in DHCPv6 by DUID concept. Each
|
||||
/// client has its own unique DUID (DHCP Unique IDentifier). Furthermore, client's
|
||||
/// HW address is not always available, because client may be behind a relay (relay
|
||||
/// stores only link-local address).
|
||||
std::vector<uint8_t> hwaddr_;
|
||||
|
||||
/// @brief client identifier
|
||||
boost::shared_ptr<DUID> duid_;
|
||||
|
||||
/// @brief preferred lifetime
|
||||
///
|
||||
/// This parameter specifies preferred lifetime since the lease was assigned/renewed
|
||||
/// (cltt), expressed in seconds.
|
||||
uint32_t preferred_lft_;
|
||||
|
||||
/// @brief valid lifetime
|
||||
///
|
||||
/// This parameter specified valid lifetime since the lease was assigned/renewed
|
||||
/// (cltt), expressed in seconds.
|
||||
uint32_t valid_lft_;
|
||||
|
||||
/// @brief T1 timer
|
||||
///
|
||||
/// Specifies renewal time. Although technically it is a property of IA container,
|
||||
/// not the address itself, since our data model does not define separate IA
|
||||
/// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
|
||||
/// for the same IA, each must have consistent T1 and T2 values. Specified in
|
||||
/// seconds since cltt.
|
||||
uint32_t t1_;
|
||||
|
||||
/// @brief T2 timer
|
||||
///
|
||||
/// Specifies rebinding time. Although technically it is a property of IA container,
|
||||
/// not the address itself, since our data model does not define separate IA
|
||||
/// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
|
||||
/// for the same IA, each must have consistent T1 and T2 values. Specified in
|
||||
/// seconds since cltt.
|
||||
uint32_t t2_;
|
||||
|
||||
/// @brief client last transmission time
|
||||
///
|
||||
/// Specifies a timestamp, when last transmission from a client was received.
|
||||
time_t cltt_;
|
||||
|
||||
/// @brief Subnet identifier
|
||||
///
|
||||
/// Specifies subnet-id of the subnet that the lease belongs to
|
||||
SubnetID subnet_id_;
|
||||
|
||||
/// @brief Is this a fixed lease?
|
||||
///
|
||||
/// Fixed leases are kept after they are released/expired.
|
||||
bool fixed_;
|
||||
|
||||
/// @brief client hostname
|
||||
///
|
||||
/// This field may be empty
|
||||
std::string hostname_;
|
||||
|
||||
/// @brief did we update AAAA record for this lease?
|
||||
bool fqdn_fwd_;
|
||||
|
||||
/// @brief did we update PTR record for this lease?
|
||||
bool fqdn_rev_;
|
||||
|
||||
/// @brief Lease comments
|
||||
///
|
||||
/// This field is currently not used.
|
||||
std::string comments_;
|
||||
|
||||
/// @todo: Add DHCPv6 failover related fields here
|
||||
};
|
||||
|
||||
/// @brief Pointer to a Lease6 structure.
|
||||
typedef boost::shared_ptr<Lease6> Lease6Ptr;
|
||||
|
||||
/// @brief Const pointer to a Lease6 structure.
|
||||
typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
|
||||
|
||||
/// @brief A collection of IPv6 leases.
|
||||
typedef std::vector< boost::shared_ptr<Lease6Ptr> > Lease6Collection;
|
||||
|
||||
/// @brief Abstract Lease Manager
|
||||
///
|
||||
/// This is an abstract API for lease database backends. It provides unified
|
||||
/// interface to all backends. As this is an abstract class, it should not
|
||||
/// be used directly, but rather specialized derived class should be used
|
||||
/// instead.
|
||||
class LeaseMgr {
|
||||
public:
|
||||
|
||||
/// Client Hardware address
|
||||
typedef std::vector<uint8_t> HWAddr;
|
||||
|
||||
/// @brief The sole lease manager constructor
|
||||
///
|
||||
/// dbconfig is a generic way of passing parameters. Parameters
|
||||
/// are passed in the "name=value" format, separated by spaces.
|
||||
/// Values may be enclosed in double quotes, if needed.
|
||||
///
|
||||
/// @param dbconfig database configuration
|
||||
LeaseMgr(const std::string& dbconfig);
|
||||
|
||||
/// @brief Destructor (closes file)
|
||||
virtual ~LeaseMgr();
|
||||
|
||||
/// @brief Adds an IPv4 lease.
|
||||
///
|
||||
/// @param lease lease to be added
|
||||
virtual bool addLease(Lease4Ptr lease) = 0;
|
||||
|
||||
/// @brief Adds an IPv6 lease.
|
||||
///
|
||||
/// @param lease lease to be added
|
||||
virtual bool addLease(Lease6Ptr lease) = 0;
|
||||
|
||||
/// @brief Returns existing IPv4 lease for specified IPv4 address and subnet_id
|
||||
///
|
||||
/// This method is used to get a lease for specific subnet_id. There can be
|
||||
/// at most one lease for any given subnet, so this method returns a single
|
||||
/// pointer.
|
||||
///
|
||||
/// @param addr address of the searched lease
|
||||
/// @param subnet_id ID of the subnet the lease must belong to
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
|
||||
SubnetID subnet_id) const = 0;
|
||||
|
||||
/// @brief Returns an IPv4 lease for specified IPv4 address
|
||||
///
|
||||
/// This method return a lease that is associated with a given address.
|
||||
/// For other query types (by hardware addr, by client-id) there can be
|
||||
/// several leases in different subnets (e.g. for mobile clients that
|
||||
/// got address in different subnets). However, for a single address
|
||||
/// there can be only one lease, so this method returns a pointer to
|
||||
/// a single lease, not a container of leases.
|
||||
///
|
||||
/// @param addr address of the searched lease
|
||||
/// @param subnet_id ID of the subnet the lease must belong to
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const = 0;
|
||||
|
||||
/// @brief Returns existing IPv4 leases for specified hardware address.
|
||||
///
|
||||
/// Although in the usual case there will be only one lease, for mobile
|
||||
/// clients or clients with multiple static/fixed/reserved leases there
|
||||
/// can be more than one. Thus return type is a container, not a single
|
||||
/// pointer.
|
||||
///
|
||||
/// @param hwaddr hardware address of the client
|
||||
///
|
||||
/// @return lease collection
|
||||
virtual Lease4Collection getLease4(const HWAddr& hwaddr) const = 0;
|
||||
|
||||
/// @brief Returns existing IPv4 leases for specified hardware address
|
||||
/// and a subnet
|
||||
///
|
||||
/// There can be at most one lease for a given HW address in a single
|
||||
/// pool, so this method with either return a single lease or NULL.
|
||||
///
|
||||
/// @param hwaddr hardware address of the client
|
||||
/// @param subnet_id identifier of the subnet that lease must belong to
|
||||
///
|
||||
/// @return a pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
|
||||
SubnetID subnet_id) const = 0;
|
||||
|
||||
/// @brief Returns existing IPv4 lease for specified client-id
|
||||
///
|
||||
/// Although in the usual case there will be only one lease, for mobile
|
||||
/// clients or clients with multiple static/fixed/reserved leases there
|
||||
/// can be more than one. Thus return type is a container, not a single
|
||||
/// pointer.
|
||||
///
|
||||
/// @param clientid client identifier
|
||||
///
|
||||
/// @return lease collection
|
||||
virtual Lease4Collection getLease4(const ClientId& clientid) const = 0;
|
||||
|
||||
/// @brief Returns existing IPv4 lease for specified client-id
|
||||
///
|
||||
/// There can be at most one lease for a given HW address in a single
|
||||
/// pool, so this method with either return a single lease or NULL.
|
||||
///
|
||||
/// @param clientid client identifier
|
||||
/// @param subnet_id identifier of the subnet that lease must belong to
|
||||
///
|
||||
/// @return a pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease4Ptr getLease4(const ClientId& clientid,
|
||||
SubnetID subnet_id) const = 0;
|
||||
|
||||
/// @brief Returns existing IPv6 lease for a given IPv6 address.
|
||||
///
|
||||
/// For a given address, we assume that there will be only one lease.
|
||||
/// The assumtion here is that there will not be site or link-local
|
||||
/// addresses used, so there is no way of having address duplication.
|
||||
///
|
||||
/// @param addr address of the searched lease
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const = 0;
|
||||
|
||||
/// @brief Returns existing IPv6 leases for a given DUID+IA combination
|
||||
///
|
||||
/// Although in the usual case there will be only one lease, for mobile
|
||||
/// clients or clients with multiple static/fixed/reserved leases there
|
||||
/// can be more than one. Thus return type is a container, not a single
|
||||
/// pointer.
|
||||
///
|
||||
/// @param duid client DUID
|
||||
/// @param iaid IA identifier
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease6Collection getLease6(const DUID& duid,
|
||||
uint32_t iaid) const = 0;
|
||||
|
||||
/// @brief Returns existing IPv6 lease for a given DUID+IA combination
|
||||
///
|
||||
/// @param duid client DUID
|
||||
/// @param iaid IA identifier
|
||||
/// @param subnet_id subnet id of the subnet the lease belongs to
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
|
||||
SubnetID subnet_id) const = 0;
|
||||
|
||||
/// @brief Updates IPv4 lease.
|
||||
///
|
||||
/// @param lease4 The lease to be updated.
|
||||
///
|
||||
/// If no such lease is present, an exception will be thrown.
|
||||
virtual void updateLease4(Lease4Ptr lease4) = 0;
|
||||
|
||||
/// @brief Updates IPv4 lease.
|
||||
///
|
||||
/// @param lease4 The lease to be updated.
|
||||
///
|
||||
/// If no such lease is present, an exception will be thrown.
|
||||
virtual void updateLease6(Lease6Ptr lease6) = 0;
|
||||
|
||||
/// @brief Deletes a lease.
|
||||
///
|
||||
/// @param addr IPv4 address of the lease to be deleted.
|
||||
///
|
||||
/// @return true if deletion was successful, false if no such lease exists
|
||||
virtual bool deleteLease4(uint32_t addr) = 0;
|
||||
|
||||
/// @brief Deletes a lease.
|
||||
///
|
||||
/// @param addr IPv4 address of the lease to be deleted.
|
||||
///
|
||||
/// @return true if deletion was successful, false if no such lease exists
|
||||
virtual bool deleteLease6(isc::asiolink::IOAddress addr) = 0;
|
||||
|
||||
/// @brief Returns backend name.
|
||||
///
|
||||
/// Each backend have specific name, e.g. "mysql" or "sqlite".
|
||||
virtual std::string getName() const = 0;
|
||||
|
||||
/// @brief Returns description of the backend.
|
||||
///
|
||||
/// This description may be multiline text that describes the backend.
|
||||
virtual std::string getDescription() const = 0;
|
||||
|
||||
/// @brief Returns backend version.
|
||||
///
|
||||
/// @todo: We will need to implement 3 version functions eventually:
|
||||
/// A. abstract API version
|
||||
/// B. backend version
|
||||
/// C. database version (stored in the database scheme)
|
||||
///
|
||||
/// and then check that:
|
||||
/// B>=A and B=C (it is ok to have newer backend, as it should be backward
|
||||
/// compatible)
|
||||
/// Also if B>C, some database upgrade procedure may be triggered
|
||||
virtual std::string getVersion() const = 0;
|
||||
|
||||
/// @todo: Add host management here
|
||||
/// As host reservation is outside of scope for 2012, support for hosts
|
||||
/// is currently postponed.
|
||||
|
||||
protected:
|
||||
/// @brief returns value of the parameter
|
||||
std::string getParameter(const std::string& name) const;
|
||||
|
||||
/// @brief list of parameters passed in dbconfig
|
||||
///
|
||||
/// That will be mostly used for storing database name, username,
|
||||
/// password and other parameters required for DB access. It is not
|
||||
/// intended to keep any DHCP-related parameters.
|
||||
std::map<std::string, std::string> parameters_;
|
||||
};
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
|
||||
}; // end of isc namespace
|
@ -30,6 +30,38 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
|
||||
return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
|
||||
}
|
||||
|
||||
Pool4::Pool4(const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last)
|
||||
:Pool(first, last) {
|
||||
// check if specified address boundaries are sane
|
||||
if (first.getFamily() != AF_INET || last.getFamily() != AF_INET) {
|
||||
isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
|
||||
}
|
||||
|
||||
if (last < first) {
|
||||
isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
|
||||
}
|
||||
}
|
||||
|
||||
Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len)
|
||||
:Pool(prefix, IOAddress("0.0.0.0")) {
|
||||
|
||||
// check if the prefix is sane
|
||||
if (prefix.getFamily() != AF_INET) {
|
||||
isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
|
||||
}
|
||||
|
||||
// check if the prefix length is sane
|
||||
if (prefix_len == 0 || prefix_len > 32) {
|
||||
isc_throw(BadValue, "Invalid prefix length");
|
||||
}
|
||||
|
||||
// Let's now calculate the last address in defined pool
|
||||
last_ = lastAddrInPrefix(prefix, prefix_len);
|
||||
}
|
||||
|
||||
|
||||
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last)
|
||||
:Pool(first, last), type_(type), prefix_len_(0) {
|
||||
@ -52,7 +84,6 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
|
||||
// last_ = first;
|
||||
}
|
||||
|
||||
|
||||
// TYPE_PD is not supported by this constructor. first-last style
|
||||
// parameters are for IA and TA only. There is another dedicated
|
||||
// constructor for that (it uses prefix/length)
|
||||
|
@ -91,6 +91,33 @@ protected:
|
||||
std::string comments_;
|
||||
};
|
||||
|
||||
/// @brief Pool information for IPv4 addresses
|
||||
///
|
||||
/// It holds information about pool4, i.e. a range of IPv4 address space that
|
||||
/// is configured for DHCP allocation.
|
||||
class Pool4 : public Pool {
|
||||
public:
|
||||
/// @brief the constructor for Pool4 "min-max" style definition
|
||||
///
|
||||
/// @param first the first address in a pool
|
||||
/// @param last the last address in a pool
|
||||
Pool4(const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last);
|
||||
|
||||
/// @brief the constructor for Pool4 "prefix/len" style definition
|
||||
///
|
||||
/// @param prefix specifies prefix of the pool
|
||||
/// @param prefix_len specifies length of the prefix of the pool
|
||||
Pool4(const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len);
|
||||
};
|
||||
|
||||
/// @brief a pointer an IPv4 Pool
|
||||
typedef boost::shared_ptr<Pool4> Pool4Ptr;
|
||||
|
||||
/// @brief a container for IPv4 Pools
|
||||
typedef std::vector<Pool4Ptr> Pool4Collection;
|
||||
|
||||
/// @brief Pool information for IPv6 addresses and prefixes
|
||||
///
|
||||
/// It holds information about pool6, i.e. a range of IPv6 address space that
|
||||
|
@ -41,6 +41,50 @@ bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
|
||||
return ((first <= addr) && (addr <= last));
|
||||
}
|
||||
|
||||
Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
|
||||
const Triplet<uint32_t>& t1,
|
||||
const Triplet<uint32_t>& t2,
|
||||
const Triplet<uint32_t>& valid_lifetime)
|
||||
:Subnet(prefix, length, t1, t2, valid_lifetime) {
|
||||
if (prefix.getFamily() != AF_INET) {
|
||||
isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
|
||||
<< " specified in subnet4");
|
||||
}
|
||||
}
|
||||
|
||||
void Subnet4::addPool4(const Pool4Ptr& pool) {
|
||||
IOAddress first_addr = pool->getFirstAddress();
|
||||
IOAddress last_addr = pool->getLastAddress();
|
||||
|
||||
if (!inRange(first_addr) || !inRange(last_addr)) {
|
||||
isc_throw(BadValue, "Pool4 (" << first_addr.toText() << "-" << last_addr.toText()
|
||||
<< " does not belong in this (" << prefix_ << "/" << prefix_len_
|
||||
<< ") subnet4");
|
||||
}
|
||||
|
||||
/// @todo: Check that pools do not overlap
|
||||
|
||||
pools_.push_back(pool);
|
||||
}
|
||||
|
||||
Pool4Ptr Subnet4::getPool4(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
|
||||
Pool4Ptr candidate;
|
||||
for (Pool4Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
|
||||
|
||||
// if we won't find anything better, then let's just use the first pool
|
||||
if (!candidate) {
|
||||
candidate = *pool;
|
||||
}
|
||||
|
||||
// if the client provided a pool and there's a pool that hint is valid in,
|
||||
// then let's use that pool
|
||||
if ((*pool)->inRange(hint)) {
|
||||
return (*pool);
|
||||
}
|
||||
}
|
||||
return (candidate);
|
||||
}
|
||||
|
||||
Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
|
||||
const Triplet<uint32_t>& t1,
|
||||
const Triplet<uint32_t>& t2,
|
||||
|
@ -93,6 +93,57 @@ protected:
|
||||
Triplet<uint32_t> valid_;
|
||||
};
|
||||
|
||||
/// @brief A configuration holder for IPv4 subnet.
|
||||
///
|
||||
/// This class represents an IPv4 subnet.
|
||||
class Subnet4 : public Subnet {
|
||||
public:
|
||||
|
||||
/// @brief Constructor with all parameters
|
||||
///
|
||||
/// @param prefix Subnet4 prefix
|
||||
/// @param length prefix length
|
||||
/// @param t1 renewal timer (in seconds)
|
||||
/// @param t2 rebind timer (in seconds)
|
||||
/// @param valid_lifetime preferred lifetime of leases (in seconds)
|
||||
Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
|
||||
const Triplet<uint32_t>& t1,
|
||||
const Triplet<uint32_t>& t2,
|
||||
const Triplet<uint32_t>& valid_lifetime);
|
||||
|
||||
/// @brief Returns a pool that specified address belongs to
|
||||
///
|
||||
/// @param hint address that the returned pool should cover (optional)
|
||||
/// @return Pointer to found pool4 (or NULL)
|
||||
Pool4Ptr getPool4(const isc::asiolink::IOAddress& hint =
|
||||
isc::asiolink::IOAddress("0.0.0.0"));
|
||||
|
||||
/// @brief Adds a new pool.
|
||||
/// @param pool pool to be added
|
||||
void addPool4(const Pool4Ptr& pool);
|
||||
|
||||
/// @brief returns all pools
|
||||
///
|
||||
/// The reference is only valid as long as the object that
|
||||
/// returned it.
|
||||
///
|
||||
/// @return a collection of all pools
|
||||
const Pool4Collection& getPools() const {
|
||||
return pools_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @brief collection of pools in that list
|
||||
Pool4Collection pools_;
|
||||
};
|
||||
|
||||
/// @brief A pointer to a Subnet4 object
|
||||
typedef boost::shared_ptr<Subnet4> Subnet4Ptr;
|
||||
|
||||
/// @brief A collection of Subnet6 objects
|
||||
typedef std::vector<Subnet4Ptr> Subnet4Collection;
|
||||
|
||||
|
||||
/// @brief A configuration holder for IPv6 subnet.
|
||||
///
|
||||
/// This class represents an IPv6 subnet.
|
||||
|
@ -28,6 +28,7 @@ TESTS += libdhcp++_unittests libdhcpsrv_unittests
|
||||
libdhcp___unittests_SOURCES = run_unittests.cc
|
||||
libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
|
||||
libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
|
||||
libdhcp___unittests_SOURCES += lease_mgr_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_ia_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
|
||||
@ -35,6 +36,7 @@ libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option_unittest.cc
|
||||
libdhcp___unittests_SOURCES += pkt6_unittest.cc
|
||||
libdhcp___unittests_SOURCES += pkt4_unittest.cc
|
||||
libdhcp___unittests_SOURCES += duid_unittest.cc
|
||||
|
||||
libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
|
||||
libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
|
@ -26,7 +26,67 @@ using namespace std;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::asiolink;
|
||||
|
||||
TEST(Pool6Test, lastAddrInPrefix) {
|
||||
// This test verifies that lastAddrInPrefix is able to handle IPv4 operations.
|
||||
TEST(AddrUtilitiesTest, lastAddrInPrefix4) {
|
||||
IOAddress addr1("192.0.2.1");
|
||||
|
||||
// Prefixes rounded to addresses are easy...
|
||||
EXPECT_EQ("192.255.255.255", lastAddrInPrefix(addr1, 8).toText());
|
||||
EXPECT_EQ("192.0.255.255", lastAddrInPrefix(addr1, 16).toText());
|
||||
EXPECT_EQ("192.0.2.255", lastAddrInPrefix(addr1, 24).toText());
|
||||
|
||||
// these are trickier
|
||||
EXPECT_EQ("192.0.2.127", lastAddrInPrefix(addr1, 25).toText());
|
||||
EXPECT_EQ("192.0.2.63", lastAddrInPrefix(addr1, 26).toText());
|
||||
EXPECT_EQ("192.0.2.31", lastAddrInPrefix(addr1, 27).toText());
|
||||
EXPECT_EQ("192.0.2.15", lastAddrInPrefix(addr1, 28).toText());
|
||||
EXPECT_EQ("192.0.2.7", lastAddrInPrefix(addr1, 29).toText());
|
||||
EXPECT_EQ("192.0.2.3", lastAddrInPrefix(addr1, 30).toText());
|
||||
|
||||
// that doesn't make much sense as /31 subnet consists of network address
|
||||
// and a broadcast address, with 0 usable addresses.
|
||||
EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 31).toText());
|
||||
EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 32).toText());
|
||||
|
||||
// Let's check extreme cases
|
||||
IOAddress anyAddr("0.0.0.0");
|
||||
EXPECT_EQ("127.255.255.255", lastAddrInPrefix(anyAddr, 1).toText());
|
||||
EXPECT_EQ("255.255.255.255", lastAddrInPrefix(anyAddr, 0).toText());
|
||||
EXPECT_EQ("0.0.0.0", lastAddrInPrefix(anyAddr, 32).toText());
|
||||
}
|
||||
|
||||
// This test checks if firstAddrInPrefix is able to handle IPv4 operations.
|
||||
TEST(AddrUtilitiesTest, firstAddrInPrefix4) {
|
||||
IOAddress addr1("192.223.2.255");
|
||||
|
||||
// Prefixes rounded to addresses are easy...
|
||||
EXPECT_EQ("192.0.0.0", firstAddrInPrefix(addr1, 8).toText());
|
||||
EXPECT_EQ("192.223.0.0", firstAddrInPrefix(addr1, 16).toText());
|
||||
EXPECT_EQ("192.223.2.0", firstAddrInPrefix(addr1, 24).toText());
|
||||
|
||||
// these are trickier
|
||||
EXPECT_EQ("192.223.2.128", firstAddrInPrefix(addr1, 25).toText());
|
||||
EXPECT_EQ("192.223.2.192", firstAddrInPrefix(addr1, 26).toText());
|
||||
EXPECT_EQ("192.223.2.224", firstAddrInPrefix(addr1, 27).toText());
|
||||
EXPECT_EQ("192.223.2.240", firstAddrInPrefix(addr1, 28).toText());
|
||||
EXPECT_EQ("192.223.2.248", firstAddrInPrefix(addr1, 29).toText());
|
||||
EXPECT_EQ("192.223.2.252", firstAddrInPrefix(addr1, 30).toText());
|
||||
|
||||
// that doesn't make much sense as /31 subnet consists of network address
|
||||
// and a broadcast address, with 0 usable addresses.
|
||||
EXPECT_EQ("192.223.2.254", firstAddrInPrefix(addr1, 31).toText());
|
||||
EXPECT_EQ("192.223.2.255", firstAddrInPrefix(addr1, 32).toText());
|
||||
|
||||
// Let's check extreme cases.
|
||||
IOAddress bcast("255.255.255.255");
|
||||
EXPECT_EQ("128.0.0.0", firstAddrInPrefix(bcast, 1).toText());
|
||||
EXPECT_EQ("0.0.0.0", firstAddrInPrefix(bcast, 0).toText());
|
||||
EXPECT_EQ("255.255.255.255", firstAddrInPrefix(bcast, 32).toText());
|
||||
|
||||
}
|
||||
|
||||
/// This test checks if lastAddrInPrefix properly supports IPv6 operations
|
||||
TEST(AddrUtilitiesTest, lastAddrInPrefix6) {
|
||||
IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef");
|
||||
|
||||
// Prefixes rounded to nibbles are easy...
|
||||
@ -63,7 +123,8 @@ TEST(Pool6Test, lastAddrInPrefix) {
|
||||
EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText());
|
||||
}
|
||||
|
||||
TEST(Pool6Test, firstAddrInPrefix) {
|
||||
/// This test checks if firstAddrInPrefix properly supports IPv6 operations
|
||||
TEST(AddrUtilitiesTest, firstAddrInPrefix6) {
|
||||
IOAddress addr1("2001:db8:1:1234:5678:1234:abcd:beef");
|
||||
|
||||
// Prefixes rounded to nibbles are easy...
|
||||
|
@ -32,6 +32,38 @@ using boost::scoped_ptr;
|
||||
|
||||
namespace {
|
||||
|
||||
// This test verifies if the configuration manager is able to hold and return
|
||||
// valid leases
|
||||
TEST(CfgMgrTest, subnet4) {
|
||||
CfgMgr& cfg_mgr = CfgMgr::instance();
|
||||
|
||||
ASSERT_TRUE(&cfg_mgr != 0);
|
||||
|
||||
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
|
||||
Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
|
||||
Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
|
||||
|
||||
// there shouldn't be any subnet configured at this stage
|
||||
EXPECT_EQ( Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.0")));
|
||||
|
||||
cfg_mgr.addSubnet4(subnet1);
|
||||
|
||||
// Now we have only one subnet, any request will be served from it
|
||||
EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.63")));
|
||||
|
||||
// Now we add more subnets and check that both old and new subnets
|
||||
// are accessible.
|
||||
cfg_mgr.addSubnet4(subnet2);
|
||||
cfg_mgr.addSubnet4(subnet3);
|
||||
|
||||
EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.191")));
|
||||
EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.15")));
|
||||
EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.85")));
|
||||
|
||||
// Try to find an address that does not belong to any subnet
|
||||
EXPECT_EQ(Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.192")));
|
||||
}
|
||||
|
||||
// This test verifies if the configuration manager is able to hold and return
|
||||
// valid leases
|
||||
TEST(CfgMgrTest, subnet6) {
|
||||
@ -58,6 +90,10 @@ TEST(CfgMgrTest, subnet6) {
|
||||
EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef")));
|
||||
EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1")));
|
||||
|
||||
cfg_mgr.deleteSubnets6();
|
||||
EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("200::123")));
|
||||
EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("3000::123")));
|
||||
EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("4000::123")));
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
169
src/lib/dhcp/tests/duid_unittest.cc
Normal file
169
src/lib/dhcp/tests/duid_unittest.cc
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright (C) 2011 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 <config.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <arpa/inet.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/duid.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::asiolink;
|
||||
|
||||
// don't import the entire boost namespace. It will unexpectedly hide uint8_t
|
||||
// for some systems.
|
||||
using boost::scoped_ptr;
|
||||
|
||||
namespace {
|
||||
|
||||
// This test verifies if the constructors are working as expected
|
||||
// and process passed parameters.
|
||||
TEST(DuidTest, constructor) {
|
||||
|
||||
uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
|
||||
vector<uint8_t> data2(data1, data1 + sizeof(data1));
|
||||
|
||||
scoped_ptr<DUID> duid1(new DUID(data1, sizeof(data1)));
|
||||
scoped_ptr<DUID> duid2(new DUID(data2));
|
||||
|
||||
vector<uint8_t> vecdata = duid1->getDuid();
|
||||
EXPECT_TRUE(data2 == vecdata);
|
||||
EXPECT_EQ(DUID::DUID_LLT, duid1->getType());
|
||||
|
||||
vecdata = duid2->getDuid();
|
||||
EXPECT_TRUE(data2 == vecdata);
|
||||
|
||||
EXPECT_EQ(DUID::DUID_LLT, duid2->getType());
|
||||
}
|
||||
|
||||
// This test verifies if DUID size restrictions are implemented
|
||||
// properly.
|
||||
TEST(DuidTest, size) {
|
||||
const int MAX_DUID_SIZE = 128;
|
||||
uint8_t data[MAX_DUID_SIZE + 1];
|
||||
vector<uint8_t> data2;
|
||||
for (uint8_t i = 0; i < MAX_DUID_SIZE + 1; ++i) {
|
||||
data[i] = i;
|
||||
if (i < MAX_DUID_SIZE)
|
||||
data2.push_back(i);
|
||||
}
|
||||
ASSERT_EQ(data2.size(), MAX_DUID_SIZE);
|
||||
|
||||
scoped_ptr<DUID> duidmaxsize1(new DUID(data, MAX_DUID_SIZE));
|
||||
scoped_ptr<DUID> duidmaxsize2(new DUID(data2));
|
||||
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_SIZE + 1)),
|
||||
OutOfRange);
|
||||
|
||||
// that's one too much
|
||||
data2.push_back(128);
|
||||
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<DUID> toolarge2(new DUID(data2)),
|
||||
OutOfRange);
|
||||
}
|
||||
|
||||
// This test verifies if the implementation supports all defined
|
||||
// DUID types.
|
||||
TEST(DuidTest, getType) {
|
||||
uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
uint8_t en[] = {0, 2, 2, 3, 4, 5, 6};
|
||||
uint8_t ll[] = {0, 3, 2, 3, 4, 5, 6};
|
||||
uint8_t uuid[] = {0, 4, 2, 3, 4, 5, 6};
|
||||
uint8_t invalid[] = {0,55, 2, 3, 4, 5, 6};
|
||||
|
||||
scoped_ptr<DUID> duid_llt(new DUID(llt, sizeof(llt)));
|
||||
scoped_ptr<DUID> duid_en(new DUID(en, sizeof(en)));
|
||||
scoped_ptr<DUID> duid_ll(new DUID(ll, sizeof(ll)));
|
||||
scoped_ptr<DUID> duid_uuid(new DUID(uuid, sizeof(uuid)));
|
||||
scoped_ptr<DUID> duid_invalid(new DUID(invalid, sizeof(invalid)));
|
||||
|
||||
EXPECT_EQ(DUID::DUID_LLT, duid_llt->getType());
|
||||
EXPECT_EQ(DUID::DUID_EN, duid_en->getType());
|
||||
EXPECT_EQ(DUID::DUID_LL, duid_ll->getType());
|
||||
EXPECT_EQ(DUID::DUID_UUID, duid_uuid->getType());
|
||||
EXPECT_EQ(DUID::DUID_UNKNOWN, duid_invalid->getType());
|
||||
}
|
||||
|
||||
// This test checks if the comparison operators are sane.
|
||||
TEST(DuidTest, operators) {
|
||||
uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
uint8_t data2[] = {0, 1, 2, 3, 4};
|
||||
uint8_t data3[] = {0, 1, 2, 3, 4, 5, 7}; // last digit different
|
||||
uint8_t data4[] = {0, 1, 2, 3, 4, 5, 6}; // the same as 1
|
||||
|
||||
scoped_ptr<DUID> duid1(new DUID(data1, sizeof(data1)));
|
||||
scoped_ptr<DUID> duid2(new DUID(data2, sizeof(data2)));
|
||||
scoped_ptr<DUID> duid3(new DUID(data3, sizeof(data3)));
|
||||
scoped_ptr<DUID> duid4(new DUID(data4, sizeof(data4)));
|
||||
|
||||
EXPECT_TRUE(*duid1 == *duid4);
|
||||
EXPECT_FALSE(*duid1 == *duid2);
|
||||
EXPECT_FALSE(*duid1 == *duid3);
|
||||
|
||||
EXPECT_FALSE(*duid1 != *duid4);
|
||||
EXPECT_TRUE(*duid1 != *duid2);
|
||||
EXPECT_TRUE(*duid1 != *duid3);
|
||||
}
|
||||
|
||||
// This test verifies if the ClientId constructors are working properly
|
||||
// and passed parameters are used
|
||||
TEST(ClientIdTest, constructor) {
|
||||
IOAddress addr2("192.0.2.1");
|
||||
IOAddress addr3("2001:db8:1::1");
|
||||
|
||||
uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
vector<uint8_t> data2(data1, data1 + sizeof(data1));
|
||||
|
||||
// checks for C-style construtor (uint8_t * + len)
|
||||
scoped_ptr<ClientId> id1(new ClientId(data1, sizeof(data1)));
|
||||
vector<uint8_t> vecdata = id1->getClientId();
|
||||
EXPECT_TRUE(data2 == vecdata);
|
||||
|
||||
// checks for vector-based constructor
|
||||
scoped_ptr<ClientId> id2(new ClientId(data2));
|
||||
vecdata = id2->getClientId();
|
||||
EXPECT_TRUE(data2 == vecdata);
|
||||
}
|
||||
|
||||
// This test checks if the comparison operators are sane.
|
||||
TEST(ClientIdTest, operators) {
|
||||
uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
uint8_t data2[] = {0, 1, 2, 3, 4};
|
||||
uint8_t data3[] = {0, 1, 2, 3, 4, 5, 7}; // last digit different
|
||||
uint8_t data4[] = {0, 1, 2, 3, 4, 5, 6}; // the same as 1
|
||||
|
||||
scoped_ptr<ClientId> id1(new ClientId(data1, sizeof(data1)));
|
||||
scoped_ptr<ClientId> id2(new ClientId(data2, sizeof(data2)));
|
||||
scoped_ptr<ClientId> id3(new ClientId(data3, sizeof(data3)));
|
||||
scoped_ptr<ClientId> id4(new ClientId(data4, sizeof(data4)));
|
||||
|
||||
EXPECT_TRUE(*id1 == *id4);
|
||||
EXPECT_FALSE(*id1 == *id2);
|
||||
EXPECT_FALSE(*id1 == *id3);
|
||||
|
||||
EXPECT_FALSE(*id1 != *id4);
|
||||
EXPECT_TRUE(*id1 != *id2);
|
||||
EXPECT_TRUE(*id1 != *id3);
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
296
src/lib/dhcp/tests/lease_mgr_unittest.cc
Normal file
296
src/lib/dhcp/tests/lease_mgr_unittest.cc
Normal file
@ -0,0 +1,296 @@
|
||||
// Copyright (C) 2011-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 <config.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/lease_mgr.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
|
||||
// This is a concrete implementation of a Lease database.
|
||||
// It does not do anything useful now, and is used for abstract LeaseMgr
|
||||
// class testing. It may later evolve into more useful backend if the
|
||||
// need arises. We can reuse code from memfile benchmark. See code in
|
||||
// tests/tools/dhcp-ubench/memfile_bench.{cc|h}
|
||||
class Memfile_LeaseMgr : public LeaseMgr {
|
||||
public:
|
||||
|
||||
/// @brief The sole lease manager constructor
|
||||
///
|
||||
/// dbconfig is a generic way of passing parameters. Parameters
|
||||
/// are passed in the "name=value" format, separated by spaces.
|
||||
/// Values may be enclosed in double quotes, if needed.
|
||||
///
|
||||
/// @param dbconfig database configuration
|
||||
Memfile_LeaseMgr(const std::string& dbconfig);
|
||||
|
||||
/// @brief Destructor (closes file)
|
||||
virtual ~Memfile_LeaseMgr();
|
||||
|
||||
/// @brief Adds an IPv4 lease.
|
||||
///
|
||||
/// @param lease lease to be added
|
||||
virtual bool addLease(Lease4Ptr lease);
|
||||
|
||||
/// @brief Adds an IPv6 lease.
|
||||
///
|
||||
/// @param lease lease to be added
|
||||
virtual bool addLease(Lease6Ptr lease);
|
||||
|
||||
/// @brief Returns existing IPv4 lease for specified IPv4 address.
|
||||
///
|
||||
/// @param addr address of the searched lease
|
||||
///
|
||||
/// @return a collection of leases
|
||||
virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
|
||||
|
||||
/// @brief Returns existing IPv4 lease for specific address and subnet
|
||||
/// @param addr address of the searched lease
|
||||
/// @param subnet_id ID of the subnet the lease must belong to
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
|
||||
SubnetID subnet_id) const;
|
||||
|
||||
/// @brief Returns existing IPv4 leases for specified hardware address.
|
||||
///
|
||||
/// Although in the usual case there will be only one lease, for mobile
|
||||
/// clients or clients with multiple static/fixed/reserved leases there
|
||||
/// can be more than one. Thus return type is a container, not a single
|
||||
/// pointer.
|
||||
///
|
||||
/// @param hwaddr hardware address of the client
|
||||
///
|
||||
/// @return lease collection
|
||||
virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
|
||||
|
||||
/// @brief Returns existing IPv4 leases for specified hardware address
|
||||
/// and a subnet
|
||||
///
|
||||
/// There can be at most one lease for a given HW address in a single
|
||||
/// pool, so this method with either return a single lease or NULL.
|
||||
///
|
||||
/// @param hwaddr hardware address of the client
|
||||
/// @param subnet_id identifier of the subnet that lease must belong to
|
||||
///
|
||||
/// @return a pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
|
||||
SubnetID subnet_id) const;
|
||||
|
||||
/// @brief Returns existing IPv4 lease for specified client-id
|
||||
///
|
||||
/// @param clientid client identifier
|
||||
virtual Lease4Collection getLease4(const ClientId& clientid) const;
|
||||
|
||||
/// @brief Returns existing IPv4 lease for specified client-id
|
||||
///
|
||||
/// There can be at most one lease for a given HW address in a single
|
||||
/// pool, so this method with either return a single lease or NULL.
|
||||
///
|
||||
/// @param clientid client identifier
|
||||
/// @param subnet_id identifier of the subnet that lease must belong to
|
||||
///
|
||||
/// @return a pointer to the lease (or NULL if a lease is not found)
|
||||
virtual Lease4Ptr getLease4(const ClientId& clientid,
|
||||
SubnetID subnet_id) const;
|
||||
|
||||
/// @brief Returns existing IPv6 lease for a given IPv6 address.
|
||||
///
|
||||
/// @param addr address of the searched lease
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const;
|
||||
|
||||
/// @brief Returns existing IPv6 lease for a given DUID+IA combination
|
||||
///
|
||||
/// @param duid client DUID
|
||||
/// @param iaid IA identifier
|
||||
///
|
||||
/// @return collection of IPv6 leases
|
||||
Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const;
|
||||
|
||||
/// @brief Returns existing IPv6 lease for a given DUID+IA combination
|
||||
///
|
||||
/// @param duid client DUID
|
||||
/// @param iaid IA identifier
|
||||
/// @param subnet_id identifier of the subnet the lease must belong to
|
||||
///
|
||||
/// @return smart pointer to the lease (or NULL if a lease is not found)
|
||||
Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const;
|
||||
|
||||
/// @brief Updates IPv4 lease.
|
||||
///
|
||||
/// @param lease4 The lease to be updated.
|
||||
///
|
||||
/// If no such lease is present, an exception will be thrown.
|
||||
void updateLease4(Lease4Ptr lease4);
|
||||
|
||||
/// @brief Updates IPv4 lease.
|
||||
///
|
||||
/// @param lease4 The lease to be updated.
|
||||
///
|
||||
/// If no such lease is present, an exception will be thrown.
|
||||
void updateLease6(Lease6Ptr lease6);
|
||||
|
||||
/// @brief Deletes a lease.
|
||||
///
|
||||
/// @param addr IPv4 address of the lease to be deleted.
|
||||
///
|
||||
/// @return true if deletion was successful, false if no such lease exists
|
||||
bool deleteLease4(uint32_t addr);
|
||||
|
||||
/// @brief Deletes a lease.
|
||||
///
|
||||
/// @param addr IPv4 address of the lease to be deleted.
|
||||
///
|
||||
/// @return true if deletion was successful, false if no such lease exists
|
||||
bool deleteLease6(isc::asiolink::IOAddress addr);
|
||||
|
||||
/// @brief Returns backend name.
|
||||
///
|
||||
/// Each backend have specific name, e.g. "mysql" or "sqlite".
|
||||
std::string getName() const { return "memfile"; }
|
||||
|
||||
/// @brief Returns description of the backend.
|
||||
///
|
||||
/// This description may be multiline text that describes the backend.
|
||||
std::string getDescription() const;
|
||||
|
||||
/// @brief Returns backend version.
|
||||
std::string getVersion() const { return "test-version"; }
|
||||
|
||||
using LeaseMgr::getParameter;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
};
|
||||
|
||||
Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig)
|
||||
: LeaseMgr(dbconfig) {
|
||||
}
|
||||
|
||||
Memfile_LeaseMgr::~Memfile_LeaseMgr() {
|
||||
}
|
||||
|
||||
bool Memfile_LeaseMgr::addLease(boost::shared_ptr<isc::dhcp::Lease4>) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool Memfile_LeaseMgr::addLease(boost::shared_ptr<isc::dhcp::Lease6>) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
|
||||
return (Lease4Ptr());
|
||||
}
|
||||
|
||||
Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
|
||||
return (Lease4Collection());
|
||||
}
|
||||
|
||||
Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress ,
|
||||
SubnetID) const {
|
||||
return (Lease4Ptr());
|
||||
}
|
||||
|
||||
Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&,
|
||||
SubnetID) const {
|
||||
return (Lease4Ptr());
|
||||
}
|
||||
|
||||
|
||||
Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&,
|
||||
SubnetID) const {
|
||||
return (Lease4Ptr());
|
||||
}
|
||||
|
||||
Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const {
|
||||
return (Lease4Collection());
|
||||
}
|
||||
|
||||
Lease6Ptr Memfile_LeaseMgr::getLease6(isc::asiolink::IOAddress) const {
|
||||
return (Lease6Ptr());
|
||||
}
|
||||
|
||||
Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const {
|
||||
return (Lease6Collection());
|
||||
}
|
||||
|
||||
Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID&, uint32_t,
|
||||
SubnetID) const {
|
||||
return (Lease6Ptr());
|
||||
}
|
||||
|
||||
void Memfile_LeaseMgr::updateLease4(Lease4Ptr ) {
|
||||
}
|
||||
|
||||
void Memfile_LeaseMgr::updateLease6(Lease6Ptr ) {
|
||||
|
||||
}
|
||||
|
||||
bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool Memfile_LeaseMgr::deleteLease6(isc::asiolink::IOAddress ) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
std::string Memfile_LeaseMgr::getDescription() const {
|
||||
return (string("This is a dummy memfile backend implementation.\n"
|
||||
"It does not offer any useful lease management and its only\n"
|
||||
"purpose is to test abstract lease manager API."));
|
||||
}
|
||||
|
||||
namespace {
|
||||
// empty class for now, but may be extended once Addr6 becomes bigger
|
||||
class LeaseMgrTest : public ::testing::Test {
|
||||
public:
|
||||
LeaseMgrTest() {
|
||||
}
|
||||
};
|
||||
|
||||
// This test checks if the LeaseMgr can be instantiated and that it
|
||||
// parses parameters string properly.
|
||||
TEST_F(LeaseMgrTest, constructor) {
|
||||
|
||||
// should not throw any exceptions here
|
||||
Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr("");
|
||||
delete leaseMgr;
|
||||
|
||||
leaseMgr = new Memfile_LeaseMgr("param1=value1 param2=value2");
|
||||
|
||||
EXPECT_EQ("value1", leaseMgr->getParameter("param1"));
|
||||
EXPECT_EQ("value2", leaseMgr->getParameter("param2"));
|
||||
EXPECT_THROW(leaseMgr->getParameter("param3"), BadValue);
|
||||
|
||||
delete leaseMgr;
|
||||
}
|
||||
|
||||
// There's no point in calling any other methods in LeaseMgr, as they
|
||||
// are purely virtual, so we would only call Memfile_LeaseMgr methods.
|
||||
// Those methods are just stubs that does not return anything.
|
||||
// It seems likely that we will need to extend the memfile code for
|
||||
// allocation engine tests, so we may implement tests that call
|
||||
// Memfile_LeaseMgr methods then.
|
||||
|
||||
}; // end of anonymous namespace
|
@ -27,6 +27,79 @@ using namespace isc::asiolink;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Pool4Test, constructor_first_last) {
|
||||
|
||||
// let's construct 192.0.2.1-192.0.2.255 pool
|
||||
Pool4 pool1(IOAddress("192.0.2.1"), IOAddress("192.0.2.255"));
|
||||
|
||||
EXPECT_EQ(IOAddress("192.0.2.1"), pool1.getFirstAddress());
|
||||
EXPECT_EQ(IOAddress("192.0.2.255"), pool1.getLastAddress());
|
||||
|
||||
// This is Pool4, IPv6 addresses do not belong here
|
||||
EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"),
|
||||
IOAddress("192.168.0.5")), BadValue);
|
||||
EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
|
||||
IOAddress("2001:db8::1")), BadValue);
|
||||
|
||||
// Should throw. Range should be 192.0.2.1-192.0.2.2, not
|
||||
// the other way around.
|
||||
EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.0.2.2"),
|
||||
IOAddress("192.0.2.1")), BadValue);
|
||||
}
|
||||
|
||||
TEST(Pool4Test, constructor_prefix_len) {
|
||||
|
||||
// let's construct 2001:db8:1::/96 pool
|
||||
Pool4 pool1(IOAddress("192.0.2.0"), 25);
|
||||
|
||||
EXPECT_EQ("192.0.2.0", pool1.getFirstAddress().toText());
|
||||
EXPECT_EQ("192.0.2.127", pool1.getLastAddress().toText());
|
||||
|
||||
// No such thing as /33 prefix
|
||||
EXPECT_THROW(Pool4(IOAddress("192.0.2.1"), 33), BadValue);
|
||||
|
||||
// /0 prefix does not make sense
|
||||
EXPECT_THROW(Pool4(IOAddress("192.0.2.0"), 0), BadValue);
|
||||
|
||||
// This is Pool6, IPv4 addresses do not belong here
|
||||
EXPECT_THROW(Pool4(IOAddress("2001:db8::1"), 20), BadValue);
|
||||
}
|
||||
|
||||
TEST(Pool4Test, in_range) {
|
||||
Pool4 pool1(IOAddress("192.0.2.10"), IOAddress("192.0.2.20"));
|
||||
|
||||
EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.0")));
|
||||
EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.10")));
|
||||
EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.17")));
|
||||
EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.20")));
|
||||
EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.21")));
|
||||
EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.255")));
|
||||
EXPECT_FALSE(pool1.inRange(IOAddress("255.255.255.255")));
|
||||
EXPECT_FALSE(pool1.inRange(IOAddress("0.0.0.0")));
|
||||
}
|
||||
|
||||
// This test creates 100 pools and verifies that their IDs are unique.
|
||||
TEST(Pool4Test, unique_id) {
|
||||
|
||||
const int num_pools = 100;
|
||||
std::vector<Pool4Ptr> pools;
|
||||
|
||||
for (int i = 0; i < num_pools; ++i) {
|
||||
pools.push_back(Pool4Ptr(new Pool4(IOAddress("192.0.2.0"),
|
||||
IOAddress("192.0.2.255"))));
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_pools; ++i) {
|
||||
for (int j = i + 1; j < num_pools; ++j) {
|
||||
if (pools[i]->getId() == pools[j]->getId()) {
|
||||
FAIL() << "Pool-ids must be unique";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
TEST(Pool6Test, constructor_first_last) {
|
||||
|
||||
// let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
|
||||
|
@ -29,6 +29,83 @@ using namespace isc::asiolink;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Subnet4Test, constructor) {
|
||||
EXPECT_NO_THROW(Subnet4 subnet1(IOAddress("192.0.2.2"), 16,
|
||||
1, 2, 3));
|
||||
|
||||
EXPECT_THROW(Subnet4 subnet2(IOAddress("192.0.2.0"), 33, 1, 2, 3),
|
||||
BadValue); // invalid prefix length
|
||||
EXPECT_THROW(Subnet4 subnet3(IOAddress("2001:db8::1"), 24, 1, 2, 3),
|
||||
BadValue); // IPv6 addresses are not allowed in Subnet4
|
||||
}
|
||||
|
||||
TEST(Subnet4Test, in_range) {
|
||||
Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);
|
||||
|
||||
EXPECT_EQ(1000, subnet.getT1());
|
||||
EXPECT_EQ(2000, subnet.getT2());
|
||||
EXPECT_EQ(3000, subnet.getValid());
|
||||
|
||||
EXPECT_FALSE(subnet.inRange(IOAddress("192.0.0.0")));
|
||||
EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.0")));
|
||||
EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.1")));
|
||||
EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.255")));
|
||||
EXPECT_FALSE(subnet.inRange(IOAddress("192.0.3.0")));
|
||||
EXPECT_FALSE(subnet.inRange(IOAddress("0.0.0.0")));
|
||||
EXPECT_FALSE(subnet.inRange(IOAddress("255.255.255.255")));
|
||||
}
|
||||
|
||||
TEST(Subnet4Test, Pool4InSubnet4) {
|
||||
|
||||
Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3));
|
||||
|
||||
Pool4Ptr pool1(new Pool4(IOAddress("192.1.2.0"), 25));
|
||||
Pool4Ptr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
|
||||
Pool4Ptr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
|
||||
|
||||
subnet->addPool4(pool1);
|
||||
|
||||
// If there's only one pool, get that pool
|
||||
Pool4Ptr mypool = subnet->getPool4();
|
||||
EXPECT_EQ(mypool, pool1);
|
||||
|
||||
|
||||
subnet->addPool4(pool2);
|
||||
subnet->addPool4(pool3);
|
||||
|
||||
// If there are more than one pool and we didn't provide hint, we
|
||||
// should get the first pool
|
||||
mypool = subnet->getPool4();
|
||||
|
||||
EXPECT_EQ(mypool, pool1);
|
||||
|
||||
// If we provide a hint, we should get a pool that this hint belongs to
|
||||
mypool = subnet->getPool4(IOAddress("192.1.2.195"));
|
||||
|
||||
EXPECT_EQ(mypool, pool3);
|
||||
|
||||
}
|
||||
|
||||
TEST(Subnet4Test, Subnet4_Pool4_checks) {
|
||||
|
||||
Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));
|
||||
|
||||
// this one is in subnet
|
||||
Pool4Ptr pool1(new Pool4(IOAddress("192.255.0.0"), 16));
|
||||
subnet->addPool4(pool1);
|
||||
|
||||
// this one is larger than the subnet!
|
||||
Pool4Ptr pool2(new Pool4(IOAddress("193.0.0.0"), 24));
|
||||
|
||||
EXPECT_THROW(subnet->addPool4(pool2), BadValue);
|
||||
|
||||
// this one is totally out of blue
|
||||
Pool4Ptr pool3(new Pool4(IOAddress("1.2.3.4"), 16));
|
||||
EXPECT_THROW(subnet->addPool4(pool3), BadValue);
|
||||
}
|
||||
|
||||
// Tests for Subnet6
|
||||
|
||||
TEST(Subnet6Test, constructor) {
|
||||
|
||||
EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64,
|
||||
@ -48,7 +125,6 @@ TEST(Subnet6Test, in_range) {
|
||||
EXPECT_EQ(3000, subnet.getPreferred());
|
||||
EXPECT_EQ(4000, subnet.getValid());
|
||||
|
||||
|
||||
EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
|
||||
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
|
||||
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
|
||||
@ -106,7 +182,9 @@ TEST(Subnet6Test, Subnet6_Pool6_checks) {
|
||||
Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16));
|
||||
EXPECT_THROW(subnet->addPool6(pool3), BadValue);
|
||||
|
||||
|
||||
Pool6Ptr pool4(new Pool6(Pool6::TYPE_IA, IOAddress("4001:db8:1::"), 80));
|
||||
EXPECT_THROW(subnet->addPool6(pool4), BadValue);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
@ -12,6 +12,9 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#ifndef TRIPLET_H
|
||||
#define TRIPLET_H
|
||||
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
namespace isc {
|
||||
@ -108,3 +111,5 @@ protected:
|
||||
|
||||
} // namespace isc::dhcp
|
||||
} // namespace isc
|
||||
|
||||
#endif // ifdef TRIPLET_H
|
||||
|
@ -107,7 +107,6 @@ libb10_dns___la_SOURCES += rdatafields.h rdatafields.cc
|
||||
libb10_dns___la_SOURCES += rrclass.cc
|
||||
libb10_dns___la_SOURCES += rrparamregistry.h
|
||||
libb10_dns___la_SOURCES += rrset.h rrset.cc
|
||||
libb10_dns___la_SOURCES += rrsetlist.h rrsetlist.cc
|
||||
libb10_dns___la_SOURCES += rrttl.h rrttl.cc
|
||||
libb10_dns___la_SOURCES += rrtype.cc
|
||||
libb10_dns___la_SOURCES += question.h question.cc
|
||||
|
@ -1,60 +0,0 @@
|
||||
// Copyright (C) 2010 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 <vector>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/rrtype.h>
|
||||
#include <dns/rrset.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dns {
|
||||
|
||||
void
|
||||
RRsetList::addRRset(RRsetPtr rrsetptr) {
|
||||
ConstRRsetPtr rrset_found = findRRset(rrsetptr->getType(),
|
||||
rrsetptr->getClass());
|
||||
if (rrset_found != NULL) {
|
||||
isc_throw(DuplicateRRset, "RRset is being doubly added to RRsetList: "
|
||||
"type=" << rrsetptr->getType() << ", class=" <<
|
||||
rrsetptr->getClass());
|
||||
}
|
||||
rrsets_.push_back(rrsetptr);
|
||||
}
|
||||
|
||||
void
|
||||
RRsetList::append(RRsetList& source) {
|
||||
BOOST_FOREACH(RRsetPtr rrset, source) {
|
||||
addRRset(rrset);
|
||||
}
|
||||
}
|
||||
|
||||
RRsetPtr
|
||||
RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass) {
|
||||
BOOST_FOREACH(RRsetPtr rrsetptr, rrsets_) {
|
||||
if ((rrsetptr->getClass() == rrclass) &&
|
||||
(rrsetptr->getType() == rrtype)) {
|
||||
return (rrsetptr);
|
||||
}
|
||||
}
|
||||
return (RRsetPtr());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
// Copyright (C) 2010 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 __RRSETLIST_H
|
||||
#define __RRSETLIST_H 1
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <dns/rrset.h>
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/rrtype.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dns {
|
||||
|
||||
class DuplicateRRset : public Exception {
|
||||
public:
|
||||
DuplicateRRset(const char* file, size_t line, const char* what) :
|
||||
isc::Exception(file, line, what) {}
|
||||
};
|
||||
|
||||
template <typename T, typename P, typename R>
|
||||
class RRsetListIterator :
|
||||
public std::iterator<std::input_iterator_tag, RRsetPtr> {
|
||||
public:
|
||||
RRsetListIterator() {}
|
||||
explicit RRsetListIterator(const T& it) :
|
||||
it_(it) {}
|
||||
RRsetListIterator& operator++()
|
||||
{
|
||||
++it_;
|
||||
return (*this);
|
||||
}
|
||||
RRsetListIterator operator++(int)
|
||||
{
|
||||
RRsetListIterator tmp(*this);
|
||||
++it_;
|
||||
return (tmp);
|
||||
}
|
||||
R operator*() const
|
||||
{
|
||||
return (*it_);
|
||||
}
|
||||
P operator->() const
|
||||
{
|
||||
return (&(operator*()));
|
||||
}
|
||||
bool operator==(const RRsetListIterator& other)
|
||||
{
|
||||
return (it_ == other.it_);
|
||||
}
|
||||
bool operator!=(const RRsetListIterator& other)
|
||||
{
|
||||
return (it_ != other.it_);
|
||||
}
|
||||
|
||||
private:
|
||||
T it_;
|
||||
};
|
||||
|
||||
/// A set of RRsets.
|
||||
///
|
||||
/// \note Do not use this class unless you really understand what
|
||||
/// you're doing and you're 100% sure that this class is the best choice
|
||||
/// for your purpose.
|
||||
///
|
||||
/// Counter intuitively, this class is not a "list" of RRsets but a
|
||||
/// "set" of them; it doesn't allow multiple RRsets of the same RR
|
||||
/// type and RR class to be added at the same time. And, for that
|
||||
/// reason, adding an RRset is more expensive than you'd expect. The
|
||||
/// class name is confusing, but was named so as a result of
|
||||
/// compromise: "RRsetset" would look awkward; RRsets would be
|
||||
/// confusing (with RRset).
|
||||
///
|
||||
/// In any case, if you want a list like container of RRsets, your best choice
|
||||
/// would be \c std::vector<RRset> or \c std::list<RRset>, not this class.
|
||||
/// In fact, in many cases \c RRsetList will be a suboptimal choice.
|
||||
/// This class is defined publicly as part of libdns++ for a historical
|
||||
/// reason and is actually quite specific to a particular need for libdatasrc.
|
||||
/// If you are tempted to use it, think twice to assess if this class
|
||||
/// is really what you want. Again, in many cases the answer will be no.
|
||||
class RRsetList {
|
||||
private:
|
||||
RRsetList(const RRsetList& source);
|
||||
RRsetList& operator=(const RRsetList& source);
|
||||
public:
|
||||
RRsetList() {}
|
||||
void addRRset(RRsetPtr new_rrsetptr);
|
||||
void append(RRsetList& source);
|
||||
RRsetPtr findRRset(const RRType& rrtype, const RRClass& rrclass);
|
||||
|
||||
typedef RRsetListIterator<std::vector<RRsetPtr>::iterator,
|
||||
RRsetPtr*,
|
||||
RRsetPtr&> iterator;
|
||||
typedef RRsetListIterator<std::vector<RRsetPtr>::const_iterator,
|
||||
const RRsetPtr*,
|
||||
const RRsetPtr&> const_iterator;
|
||||
|
||||
const_iterator begin() const { return (const_iterator(rrsets_.begin())); }
|
||||
const_iterator end() const { return (const_iterator(rrsets_.end())); }
|
||||
|
||||
iterator begin() { return (iterator(rrsets_.begin())); }
|
||||
iterator end() { return (iterator(rrsets_.end())); }
|
||||
|
||||
size_t size() const { return (rrsets_.size()); }
|
||||
|
||||
private:
|
||||
std::vector<RRsetPtr> rrsets_;
|
||||
};
|
||||
|
||||
} // end of namespace dns
|
||||
} // end of namespace isc
|
||||
#endif // __RRSETLIST_H
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
// End:
|
@ -56,7 +56,7 @@ run_unittests_SOURCES += rdata_minfo_unittest.cc
|
||||
run_unittests_SOURCES += rdata_tsig_unittest.cc
|
||||
run_unittests_SOURCES += rdata_naptr_unittest.cc
|
||||
run_unittests_SOURCES += rdata_hinfo_unittest.cc
|
||||
run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
|
||||
run_unittests_SOURCES += rrset_unittest.cc
|
||||
run_unittests_SOURCES += question_unittest.cc
|
||||
run_unittests_SOURCES += rrparamregistry_unittest.cc
|
||||
run_unittests_SOURCES += masterload_unittest.cc
|
||||
|
@ -1,188 +0,0 @@
|
||||
// Copyright (C) 2010 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 <vector>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <dns/rdata.h>
|
||||
#include <dns/rdataclass.h>
|
||||
#include <dns/rrclass.h>
|
||||
#include <dns/rrtype.h>
|
||||
#include <dns/rrsetlist.h>
|
||||
#include <dns/rrset.h>
|
||||
#include <dns/rrttl.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::dns;
|
||||
using namespace isc::dns::rdata;
|
||||
|
||||
namespace {
|
||||
class RRsetListTest : public ::testing::Test {
|
||||
protected:
|
||||
RRsetListTest() : example_name(Name("example.com")),
|
||||
example_ttl(RRTTL(3600))
|
||||
{}
|
||||
void setupList(RRsetList& list);
|
||||
Name example_name;
|
||||
RRTTL example_ttl;
|
||||
};
|
||||
|
||||
const in::A rdata_in_a("192.0.2.1");
|
||||
const in::AAAA rdata_in_aaaa("2001:db8::1234");
|
||||
const generic::NS rdata_ns("ns.example.com");
|
||||
const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"),
|
||||
2010012601, 3600, 300, 3600000, 1200);
|
||||
const generic::CNAME rdata_cname("target.example.com");
|
||||
const generic::DNAME rdata_dname("dtarget.example.com");
|
||||
|
||||
void
|
||||
RRsetListTest::setupList(RRsetList& list) {
|
||||
RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(),
|
||||
RRType::A(), example_ttl));
|
||||
RRsetPtr aaaa(new RRset(Name("example.com"), RRClass::IN(),
|
||||
RRType::AAAA(), example_ttl));
|
||||
RRsetPtr ns(new RRset(Name("example.com"), RRClass::IN(),
|
||||
RRType::NS(), example_ttl));
|
||||
RRsetPtr soa(new RRset(Name("example.com"), RRClass::IN(),
|
||||
RRType::SOA(), example_ttl));
|
||||
RRsetPtr cname(new RRset(Name("example.com"), RRClass::IN(),
|
||||
RRType::CNAME(), example_ttl));
|
||||
|
||||
a->addRdata(rdata_in_a);
|
||||
aaaa->addRdata(rdata_in_aaaa);
|
||||
ns->addRdata(rdata_ns);
|
||||
soa->addRdata(rdata_soa);
|
||||
cname->addRdata(rdata_cname);
|
||||
|
||||
list.addRRset(a);
|
||||
list.addRRset(aaaa);
|
||||
list.addRRset(ns);
|
||||
list.addRRset(soa);
|
||||
list.addRRset(cname);
|
||||
}
|
||||
|
||||
TEST_F(RRsetListTest, emptyOnInitialCreate) {
|
||||
RRsetList list;
|
||||
EXPECT_EQ(list.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(RRsetListTest, addRRsets) {
|
||||
RRsetList list;
|
||||
setupList(list);
|
||||
EXPECT_EQ(list.size(), 5);
|
||||
}
|
||||
|
||||
TEST_F(RRsetListTest, append) {
|
||||
RRsetList list1;
|
||||
setupList(list1);
|
||||
RRsetList list2;
|
||||
RRsetPtr dname(new RRset(Name("example.com"), RRClass::IN(),
|
||||
RRType::DNAME(), example_ttl));
|
||||
dname->addRdata(rdata_dname);
|
||||
list2.addRRset(dname);
|
||||
list1.append(list2);
|
||||
EXPECT_EQ(list2.size(), 1);
|
||||
EXPECT_EQ(list1.size(), 6);
|
||||
|
||||
RRsetPtr rrset = list1.findRRset(RRType::DNAME(), RRClass::IN());
|
||||
EXPECT_EQ(RRType::DNAME(), rrset->getType());
|
||||
|
||||
EXPECT_THROW(list1.append(list2), DuplicateRRset);
|
||||
}
|
||||
|
||||
TEST_F(RRsetListTest, extraRRset) {
|
||||
RRsetList list;
|
||||
setupList(list);
|
||||
RRsetPtr cname(new RRset(Name("another.example.com"), RRClass::IN(),
|
||||
RRType::CNAME(), example_ttl));
|
||||
EXPECT_THROW(list.addRRset(cname), DuplicateRRset);
|
||||
}
|
||||
|
||||
void
|
||||
checkFindResult(RRsetList& list, const Name& name,
|
||||
const RRType& rrtype, const RRClass& rrclass,
|
||||
const RRTTL& rrttl)
|
||||
{
|
||||
RRsetPtr rrset = list.findRRset(rrtype, rrclass);;
|
||||
EXPECT_EQ(name, rrset->getName());
|
||||
EXPECT_EQ(rrtype, rrset->getType());
|
||||
EXPECT_EQ(rrclass, rrset->getClass());
|
||||
EXPECT_EQ(rrttl, rrset->getTTL());
|
||||
}
|
||||
|
||||
TEST_F(RRsetListTest, findRRset) {
|
||||
RRsetList list;
|
||||
setupList(list);
|
||||
|
||||
checkFindResult(list, example_name, RRType::A(), RRClass::IN(),
|
||||
example_ttl);
|
||||
checkFindResult(list, example_name, RRType::CNAME(), RRClass::IN(),
|
||||
example_ttl);
|
||||
checkFindResult(list, example_name, RRType::AAAA(), RRClass::IN(),
|
||||
example_ttl);
|
||||
checkFindResult(list, example_name, RRType::NS(), RRClass::IN(),
|
||||
example_ttl);
|
||||
checkFindResult(list, example_name, RRType::SOA(), RRClass::IN(),
|
||||
example_ttl);
|
||||
}
|
||||
|
||||
TEST_F(RRsetListTest, checkData) {
|
||||
RRsetList list;
|
||||
RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(),
|
||||
RRType::A(), example_ttl));
|
||||
a->addRdata(rdata_in_a);
|
||||
list.addRRset(a);
|
||||
|
||||
RdataIteratorPtr it =
|
||||
list.findRRset(RRType::A(), RRClass::IN())->getRdataIterator();
|
||||
EXPECT_FALSE(it->isLast());
|
||||
EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
|
||||
}
|
||||
|
||||
TEST_F(RRsetListTest, iterate) {
|
||||
RRsetList list;
|
||||
setupList(list);
|
||||
|
||||
bool has_a = false, has_aaaa = false, has_ns = false, has_soa = false,
|
||||
has_cname = false;
|
||||
int i = 0;
|
||||
BOOST_FOREACH(RRsetPtr rrset, list) {
|
||||
if (rrset->getType() == RRType::A()) {
|
||||
has_a = true;
|
||||
}
|
||||
if (rrset->getType() == RRType::AAAA()) {
|
||||
has_aaaa = true;
|
||||
}
|
||||
if (rrset->getType() == RRType::NS()) {
|
||||
has_ns = true;
|
||||
}
|
||||
if (rrset->getType() == RRType::SOA()) {
|
||||
has_soa = true;
|
||||
}
|
||||
if (rrset->getType() == RRType::CNAME()) {
|
||||
has_cname = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
EXPECT_TRUE(has_a);
|
||||
EXPECT_TRUE(has_aaaa);
|
||||
EXPECT_TRUE(has_ns);
|
||||
EXPECT_TRUE(has_soa);
|
||||
EXPECT_TRUE(has_cname);
|
||||
EXPECT_TRUE(i == 5);
|
||||
}
|
||||
|
||||
}
|
@ -45,6 +45,7 @@ COMPONENT_RESTART_DELAY = 10
|
||||
|
||||
STATE_DEAD = 'dead'
|
||||
STATE_STOPPED = 'stopped'
|
||||
STATE_RESTARTING = 'restarting'
|
||||
STATE_RUNNING = 'running'
|
||||
|
||||
def get_signame(signal_number):
|
||||
@ -68,6 +69,7 @@ class BaseComponent:
|
||||
explicitly).
|
||||
- Running - after start() was called, it started successfully and is
|
||||
now running.
|
||||
- Restarting - the component failed (crashed) and is waiting for a restart
|
||||
- Dead - it failed and can not be resurrected.
|
||||
|
||||
Init
|
||||
@ -79,11 +81,11 @@ class BaseComponent:
|
||||
| | |
|
||||
|failure | failed() |
|
||||
| | |
|
||||
v | |
|
||||
v | | start()/restart()
|
||||
+<-----------+ |
|
||||
| |
|
||||
| kind == dispensable or kind|== needed and failed late
|
||||
+-----------------------------+
|
||||
+-----------------------> Restarting
|
||||
|
|
||||
| kind == core or kind == needed and it failed too soon
|
||||
v
|
||||
@ -163,7 +165,7 @@ class BaseComponent:
|
||||
"""
|
||||
if self.__state == STATE_DEAD:
|
||||
raise ValueError("Can't resurrect already dead component")
|
||||
if self.running():
|
||||
if self.is_running():
|
||||
raise ValueError("Can't start already running component")
|
||||
logger.info(BIND10_COMPONENT_START, self.name())
|
||||
self.__state = STATE_RUNNING
|
||||
@ -188,7 +190,7 @@ class BaseComponent:
|
||||
"""
|
||||
# This is not tested. It talks with the outher world, which is out
|
||||
# of scope of unittests.
|
||||
if not self.running():
|
||||
if not self.is_running():
|
||||
raise ValueError("Can't stop a component which is not running")
|
||||
logger.info(BIND10_COMPONENT_STOP, self.name())
|
||||
self.__state = STATE_STOPPED
|
||||
@ -234,9 +236,9 @@ class BaseComponent:
|
||||
|
||||
logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(),
|
||||
exit_str)
|
||||
if not self.running():
|
||||
if not self.is_running():
|
||||
raise ValueError("Can't fail component that isn't running")
|
||||
self.__state = STATE_STOPPED
|
||||
self.__state = STATE_RESTARTING # tentatively set, maybe changed to DEAD
|
||||
self._failed_internal()
|
||||
# If it is a core component or the needed component failed to start
|
||||
# (including it stopped really soon)
|
||||
@ -284,7 +286,7 @@ class BaseComponent:
|
||||
else:
|
||||
return False
|
||||
|
||||
def running(self):
|
||||
def is_running(self):
|
||||
"""
|
||||
Informs if the component is currently running. It assumes the failed
|
||||
is called whenever the component really fails and there might be some
|
||||
@ -296,6 +298,15 @@ class BaseComponent:
|
||||
"""
|
||||
return self.__state == STATE_RUNNING
|
||||
|
||||
def is_restarting(self):
|
||||
"""Informs if the component has failed and is waiting for a restart.
|
||||
|
||||
Unlike the case of is_running(), if this returns True it always means
|
||||
the corresponding process has died and not yet restarted.
|
||||
|
||||
"""
|
||||
return self.__state == STATE_RESTARTING
|
||||
|
||||
def _start_internal(self):
|
||||
"""
|
||||
This method does the actual starting of a process. You need to override
|
||||
@ -560,6 +571,13 @@ class Configurator:
|
||||
self._running = False
|
||||
self.__reconfigure_internal(self._components, {})
|
||||
|
||||
def has_component(self, component):
|
||||
'''Return if a specified component is configured.'''
|
||||
# Values of self._components are tuples of (config, component).
|
||||
# Extract the components of the tuples and see if the given one
|
||||
# is included.
|
||||
return component in map(lambda x: x[1], self._components.values())
|
||||
|
||||
def reconfigure(self, configuration):
|
||||
"""
|
||||
Changes configuration from the current one to the provided. It
|
||||
@ -591,7 +609,7 @@ class Configurator:
|
||||
for cname in old.keys():
|
||||
if cname not in new:
|
||||
component = self._components[cname][1]
|
||||
if component.running():
|
||||
if component.is_running() or component.is_restarting():
|
||||
plan.append({
|
||||
'command': STOP_CMD,
|
||||
'component': component,
|
||||
@ -674,7 +692,7 @@ class Configurator:
|
||||
self._components[task['name']] = (task['config'],
|
||||
component)
|
||||
elif command == STOP_CMD:
|
||||
if component.running():
|
||||
if component.is_running():
|
||||
component.stop()
|
||||
del self._components[task['name']]
|
||||
else:
|
||||
|
@ -214,9 +214,14 @@ class Creator(Parser):
|
||||
socket.SOCK_STREAM)
|
||||
env = copy.deepcopy(os.environ)
|
||||
env['PATH'] = path
|
||||
# We explicitly set close_fs to True; it's False by default before
|
||||
# Python 3.2. If we don't close the remaining FDs, the copy of
|
||||
# 'local' will prevent the child process from terminating when
|
||||
# the parent process died abruptly.
|
||||
self.__process = subprocess.Popen(['b10-sockcreator'], env=env,
|
||||
stdin=remote.fileno(),
|
||||
stdout=remote2.fileno(),
|
||||
close_fds=True,
|
||||
preexec_fn=self.__preexec_work)
|
||||
remote.close()
|
||||
remote2.close()
|
||||
|
@ -191,7 +191,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
self.assertFalse(self.__start_called)
|
||||
self.assertFalse(self.__stop_called)
|
||||
self.assertFalse(self.__failed_called)
|
||||
self.assertFalse(component.running())
|
||||
self.assertFalse(component.is_running())
|
||||
self.assertFalse(component.is_restarting())
|
||||
# We can't stop or fail the component yet
|
||||
self.assertRaises(ValueError, component.stop)
|
||||
self.assertRaises(ValueError, component.failed, 1)
|
||||
@ -204,7 +205,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
self.assertTrue(self.__start_called)
|
||||
self.assertFalse(self.__stop_called)
|
||||
self.assertFalse(self.__failed_called)
|
||||
self.assertTrue(component.running())
|
||||
self.assertTrue(component.is_running())
|
||||
self.assertFalse(component.is_restarting())
|
||||
|
||||
def __check_dead(self, component):
|
||||
"""
|
||||
@ -215,7 +217,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
self.assertFalse(self.__stop_called)
|
||||
self.assertTrue(self.__failed_called)
|
||||
self.assertEqual(1, self._exitcode)
|
||||
self.assertFalse(component.running())
|
||||
self.assertFalse(component.is_running())
|
||||
self.assertFalse(component.is_restarting())
|
||||
# Surely it can't be stopped when already dead
|
||||
self.assertRaises(ValueError, component.stop)
|
||||
# Nor started
|
||||
@ -234,7 +237,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
self.assertTrue(self.__start_called)
|
||||
self.assertFalse(self.__stop_called)
|
||||
self.assertTrue(self.__failed_called)
|
||||
self.assertTrue(component.running())
|
||||
self.assertTrue(component.is_running())
|
||||
self.assertFalse(component.is_restarting())
|
||||
# Check it can't be started again
|
||||
self.assertRaises(ValueError, component.start)
|
||||
|
||||
@ -246,7 +250,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
self.assertTrue(self.__start_called)
|
||||
self.assertFalse(self.__stop_called)
|
||||
self.assertTrue(self.__failed_called)
|
||||
self.assertFalse(component.running())
|
||||
self.assertFalse(component.is_running())
|
||||
self.assertTrue(component.is_restarting())
|
||||
|
||||
def __do_start_stop(self, kind):
|
||||
"""
|
||||
@ -270,7 +275,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
self.assertTrue(self.__start_called)
|
||||
self.assertTrue(self.__stop_called)
|
||||
self.assertFalse(self.__failed_called)
|
||||
self.assertFalse(component.running())
|
||||
self.assertFalse(component.is_running())
|
||||
self.assertFalse(component.is_restarting())
|
||||
# Check it can't be stopped twice
|
||||
self.assertRaises(ValueError, component.stop)
|
||||
# Or failed
|
||||
@ -553,10 +559,10 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
self.assertIsNone(component.pid())
|
||||
self.assertEqual(['hello'], component._params)
|
||||
self.assertEqual('Address', component._address)
|
||||
self.assertFalse(component.running())
|
||||
self.assertFalse(component.is_running())
|
||||
self.assertEqual({}, self.__registered_processes)
|
||||
component.start()
|
||||
self.assertTrue(component.running())
|
||||
self.assertTrue(component.is_running())
|
||||
# Some versions of unittest miss assertIsInstance
|
||||
self.assertTrue(isinstance(component._procinfo, TestProcInfo))
|
||||
self.assertEqual(42, component.pid())
|
||||
@ -580,11 +586,11 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
"""
|
||||
component = Component('component', self, 'needed', 'Address')
|
||||
component.start()
|
||||
self.assertTrue(component.running())
|
||||
self.assertTrue(component.is_running())
|
||||
self.assertEqual('component', self.__start_simple_params)
|
||||
component.pid = lambda: 42
|
||||
component.stop()
|
||||
self.assertFalse(component.running())
|
||||
self.assertFalse(component.is_running())
|
||||
self.assertEqual(('component', 'Address', 42),
|
||||
self.__stop_process_params)
|
||||
|
||||
@ -609,7 +615,7 @@ class ComponentTests(BossUtils, unittest.TestCase):
|
||||
component = Component('component', self, 'needed', 'Address',
|
||||
[], ProcInfo)
|
||||
component.start()
|
||||
self.assertTrue(component.running())
|
||||
self.assertTrue(component.is_running())
|
||||
component.kill()
|
||||
self.assertTrue(process.terminated)
|
||||
self.assertFalse(process.killed)
|
||||
@ -872,12 +878,13 @@ class ConfiguratorTest(BossUtils, unittest.TestCase):
|
||||
'priority': 6,
|
||||
'special': 'test',
|
||||
'process': 'additional',
|
||||
'kind': 'needed'
|
||||
'kind': 'dispensable' # need to be dispensable so it could restart
|
||||
}
|
||||
self.log = []
|
||||
plan = configurator._build_plan(self.__build_components(self.__core),
|
||||
bigger)
|
||||
self.assertEqual([('additional', 'init'), ('additional', 'needed')],
|
||||
self.assertEqual([('additional', 'init'),
|
||||
('additional', 'dispensable')],
|
||||
self.log)
|
||||
self.assertEqual(1, len(plan))
|
||||
self.assertTrue('component' in plan[0])
|
||||
@ -888,15 +895,29 @@ class ConfiguratorTest(BossUtils, unittest.TestCase):
|
||||
# Now remove the one component again
|
||||
# We run the plan so the component is wired into internal structures
|
||||
configurator._run_plan(plan)
|
||||
self.log = []
|
||||
plan = configurator._build_plan(self.__build_components(bigger),
|
||||
self.__core)
|
||||
self.assertEqual([], self.log)
|
||||
self.assertEqual([{
|
||||
'command': 'stop',
|
||||
'name': 'additional',
|
||||
'component': component
|
||||
}], plan)
|
||||
# We should have the 'additional' component in the configurator.
|
||||
self.assertTrue(configurator.has_component(component))
|
||||
for count in [0, 1]: # repeat two times, see below
|
||||
self.log = []
|
||||
plan = configurator._build_plan(self.__build_components(bigger),
|
||||
self.__core)
|
||||
self.assertEqual([], self.log)
|
||||
self.assertEqual([{
|
||||
'command': 'stop',
|
||||
'name': 'additional',
|
||||
'component': component
|
||||
}], plan)
|
||||
|
||||
if count is 0:
|
||||
# We then emulate unexpected failure of the component (but
|
||||
# before it restarts). It shouldn't confuse the
|
||||
# configurator in the second phase of the test
|
||||
component.failed(0)
|
||||
# Run the plan, confirm the specified component is gone.
|
||||
configurator._run_plan(plan)
|
||||
self.assertFalse(configurator.has_component(component))
|
||||
# There shouldn't be any other object that has the same name
|
||||
self.assertFalse('additional' in configurator._components)
|
||||
|
||||
# We want to switch a component. So, prepare the configurator so it
|
||||
# holds one
|
||||
|
@ -42,13 +42,17 @@ class upgradable_mutex {
|
||||
template <typename T>
|
||||
class sharable_lock {
|
||||
public:
|
||||
sharable_lock(T) { }
|
||||
sharable_lock(T) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class scoped_lock {
|
||||
public:
|
||||
scoped_lock(T) { }
|
||||
scoped_lock(T) {}
|
||||
|
||||
// We need to define this explicitly. Some versions of clang++ would
|
||||
// complain about this otherwise. See Trac ticket #2340
|
||||
~scoped_lock() {}
|
||||
|
||||
void lock() {}
|
||||
void unlock() {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user