Commit 8896c9c3 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[master] Merge branch 'trac3569_rebase' (host data source configuration)

parents bd4942ff 9f94c961
......@@ -425,17 +425,17 @@ be followed by a comma and another object definition.</para>
</section>
<section id="database-configuration4">
<title>Database Configuration</title>
<title>Lease Database Configuration</title>
<note>
<para>Database access information must be configured for the DHCPv4 server,
even if it has already been configured for the DHCPv6 server. The servers
<para>Lease database access information must be configured for the DHCPv4 server,
even if it has already been configured for the DHCPv6 server. The servers
store their information independently, so each server can use a separate
database or both servers can use the same database.</para>
</note>
<para>Database configuration is controlled through the Dhcp4/lease-database
parameters. The type of the database must be set to "mysql" or "postgresql",
<para>Lease database configuration is controlled through the Dhcp4/lease-database
parameters. The type of the database must be set to "memfile", "mysql" or "postgresql",
e.g.
<screen>
"Dhcp4": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
......@@ -471,6 +471,75 @@ be followed by a comma and another object definition.</para>
</section>
</section>
<section>
<title>Hosts Storage</title>
<note>
<para>This feature did not undergo the regular system level
testing conducted by ISC. As such, please treat it as
experimental.</para>
</note>
<para>Kea is also able to store information about host reservations in the
database. Hosts database configuration uses the same syntax as lease
database. In fact, Kea server opens independent connections for each
purpose, be it lease or hosts information. This gives the solution most
flexibility. Kea can be used to keep leases and host reservations
separately, but can also point to the same database. Currently the only
supported hosts database type is MySQL.</para>
<para>Please note that usage of hosts storage is optional. User can define
all host reservations in the configuration file. That is the recommended way
if the number of reservations is small. However, with the number of
reservations growing it's more convenient to use host storage. Please note
that both storages (configuration file and MySQL) can be used together. If
hosts are defined in both places, the definitions from configuration file
are checked first and external storage is checked later, if
necessary.</para>
<para>All hosts leases issued by the server are stored in the hosts
database. Currently there is only one available backend: MySQL. Other host
backends will become available in future Kea versions.</para>
<section id="hosts-database-configuration4">
<title>IPv4 Hosts Database Configuration</title>
<para>Hosts database configuration is controlled through the Dhcp4/hosts-database
parameters. If enabled, the type of the database must be set to "mysql". Other
hosts backends may be added in later Kea versions.
<screen>
"Dhcp4": { "hosts-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
</screen>
Next, the name of the database to hold the leases must be set: this is the
name used when the lease database was created (see <xref linkend="mysql-database-create"/>).
<screen>
"Dhcp4": { "hosts-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
</screen>
If the database is located on a different system to the DHCPv4 server, the
database host name must also be specified (although it should be noted that this
configuration may have a severe impact on server performance):
<screen>
"Dhcp4": { "hosts-database": { <userinput>"host": <replaceable>remote-host-name</replaceable></userinput>, ... }, ... }
</screen>
The usual state of affairs will be to have the database on the same machine as
the DHCPv4 server. In this case, set the value to the empty string:
<screen>
"Dhcp4": { "hosts-database": { <userinput>"host" : ""</userinput>, ... }, ... }
</screen>
</para>
<para>Finally, the credentials of the account under which the server will
access the database should be set:
<screen>
"Dhcp4": { "hosts-database": { <userinput>"user": "<replaceable>user-name</replaceable>"</userinput>,
<userinput>"password": "<replaceable>password</replaceable>"</userinput>,
... },
... }
</screen>
If there is no password to the account, set the password to the empty string
"". (This is also the default.)</para>
</section>
</section>
<section id="dhcp4-interface-configuration">
<title>Interface configuration</title>
<para>The DHCPv4 server has to be configured to listen on specific network
......
......@@ -424,18 +424,18 @@ be followed by a comma and another object definition.</para>
</section>
<section id="database-configuration6">
<title>Database Configuration</title>
<title>Lease Database Configuration</title>
<note>
<para>Database access information must be configured for the DHCPv6 server,
<para>Lease database access information must be configured for the DHCPv6 server,
even if it has already been configured for the DHCPv4 server. The servers
store their information independently, so each server can use a separate
database or both servers can use the same database.</para>
</note>
<para>Database configuration is controlled through the Dhcp6/lease-database
parameters. The type of the database must be set to "mysql" or "postgresql",
e.g.
<para>Lease database configuration is controlled through the
Dhcp6/lease-database parameters. The type of the database must be set to
"memfile", "mysql" or "postgresql", e.g.
<screen>
"Dhcp6": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
</screen>
......@@ -470,6 +470,83 @@ be followed by a comma and another object definition.</para>
</section>
</section>
<!-- Uncomment this once 4212 is merged. While the host storage for
MySQL is there, it is able to store hosts, but not IPv6
reservations. So technically we have host reservations for v6 in
MySQL, but in practice it's kinda useless right now as it can't
store addresses or prefixes. Let's keep it commented out until
we implement that functionality
<section>
<title>Hosts Storage</title>
<note>
<para>This feature did not undergo the regular system level
testing conducted by ISC. As such, please treat it as
experimental.</para>
</note>
<para>Kea is also able to store information about host reservations in the
database. Hosts database configuration uses the same syntax as lease
database. In fact, Kea server opens independent connections for each
purpose, be it lease or hosts information. This gives the solution most
flexibility. Kea can be used to keep leases and host reservations
separately, but can also point to the same database. Currently the only
supported hosts database type is MySQL.</para>
<para>Please note that usage of hosts storage is optional. User can define
all host reservations in the configuration file. That is the recommended way
if the number of reservations is small. However, with the number of
reservations growing it's more convenient to use host storage. Please note
that both storages (configuration file and MySQL) can be used together. If
hosts are defined in both places, the definitions from configuration file
are checked first and external storage is checked later, if
necessary.</para>
<para>All hosts leases issued by the server are stored in the hosts
database. Currently there is only one available backend: MySQL. Other host
backends will become available in future Kea versions.</para>
<section id="hosts-database-configuration6">
<title>IPv6 Hosts Database Configuration</title>
<para>Hosts database configuration is controlled through the Dhcp6/hosts-database
parameters. If enabled, the type of the database must be set to "mysql". Other
hosts backends may be added in later Kea versions.
<screen>
"Dhcp6": { "hosts-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
</screen>
Next, the name of the database to hold the leases must be set: this is the
name used when the lease database was created (see <xref linkend="mysql-database-create"/>).
<screen>
"Dhcp6": { "hosts-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
</screen>
If the database is located on a different system to the DHCPv6 server, the
database host name must also be specified (although it should be noted that this
configuration may have a severe impact on server performance):
<screen>
"Dhcp6": { "hosts-database": { <userinput>"host": <replaceable>remote-host-name</replaceable></userinput>, ... }, ... }
</screen>
The usual state of affairs will be to have the database on the same machine as
the DHCPv6 server. In this case, set the value to the empty string:
<screen>
"Dhcp6": { "hosts-database": { <userinput>"host" : ""</userinput>, ... }, ... }
</screen>
</para>
<para>Finally, the credentials of the account under which the server will
access the database should be set:
<screen>
"Dhcp6": { "hosts-database": { <userinput>"user": "<replaceable>user-name</replaceable>"</userinput>,
<userinput>"password": "<replaceable>password</replaceable>"</userinput>,
... },
... }
</screen>
If there is no password to the account, set the password to the empty string
"". (This is also the default.)</para>
</section>
</section>
-->
<section id="dhcp6-interface-selection">
<title>Interface selection</title>
<para>The DHCPv6 server has to be configured to listen on specific network
......
......@@ -438,7 +438,11 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
parser = new StringParser(config_id,
globalContext()->string_values_);
} else if (config_id.compare("lease-database") == 0) {
parser = new DbAccessParser(config_id, *globalContext());
parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB,
*globalContext());
} else if (config_id.compare("hosts-database") == 0) {
parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB,
*globalContext());
} else if (config_id.compare("hooks-libraries") == 0) {
parser = new HooksLibrariesParser(config_id);
} else if (config_id.compare("echo-client-id") == 0) {
......
......@@ -39,6 +39,7 @@
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/utils.h>
#include <dhcpsrv/host_mgr.h>
#include <gtest/gtest.h>
#include <stats/stats_mgr.h>
#include <boost/scoped_ptr.hpp>
......@@ -74,7 +75,31 @@ const char* CONFIGS[] = {
" \"valid-lifetime\": 4000,"
" \"interface\": \"eth0\" "
" } ],"
"\"valid-lifetime\": 4000 }"
"\"valid-lifetime\": 4000 }",
// Configuration 1:
// - 1 subnet: 192.0.2.0/24
// - MySQL Host Data Source configured
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"hosts-database\": {"
" \"type\": \"mysql\","
" \"name\": \"keatest\","
" \"user\": \"keatest\","
" \"password\": \"keatest\""
"},"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"subnet\": \"192.0.2.0/24\", "
" \"rebind-timer\": 2000, "
" \"renew-timer\": 1000, "
" \"valid-lifetime\": 4000,"
" \"interface\": \"eth0\" "
" } ],"
"\"valid-lifetime\": 4000 }"
};
// This test verifies that the destination address of the response
......@@ -2634,4 +2659,7 @@ TEST_F(Dhcpv4SrvTest, statisticsUnknownRcvd) {
EXPECT_EQ(1, drop_stat->getInteger().first);
}
/// @todo: Implement proper tests for MySQL lease/host database,
/// see ticket #4214.
}; // end of anonymous namespace
......@@ -689,7 +689,11 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
parser = new StringParser(config_id,
globalContext()->string_values_);
} else if (config_id.compare("lease-database") == 0) {
parser = new DbAccessParser(config_id, *globalContext());
parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB,
*globalContext());
} else if (config_id.compare("hosts-database") == 0) {
parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB,
*globalContext());
} else if (config_id.compare("hooks-libraries") == 0) {
parser = new HooksLibrariesParser(config_id);
} else if (config_id.compare("dhcp-ddns") == 0) {
......
......@@ -34,6 +34,7 @@
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/utils.h>
#include <util/buffer.h>
#include <util/range_utilities.h>
......@@ -61,6 +62,53 @@ using namespace std;
namespace {
const char* CONFIGS[] = {
// Configuration 0:
// - used in advertiseOptions
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth0\", "
" \"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\""
" },"
" {"
" \"name\": \"subscriber-id\","
" \"data\": \"1234\","
" \"csv-format\": False"
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }",
// Configuration 1:
// - a single subnet
// - MySQL host data source
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"hosts-database\": {"
" \"type\": \"mysql\","
" \"name\": \"keatest\","
" \"user\": \"keatest\","
" \"password\": \"keatest\""
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
" \"subnet\": \"2001:db8:1::/48\" "
" } ],"
"\"valid-lifetime\": 4000 }"
};
// This test verifies that incoming SOLICIT can be handled properly when
// there are no subnets configured.
//
......@@ -288,28 +336,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
IfaceMgrTestConfig test_config(true);
ConstElementPtr x;
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth0\", "
" \"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\""
" },"
" {"
" \"name\": \"subscriber-id\","
" \"data\": \"1234\","
" \"csv-format\": False"
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }";
ASSERT_NO_THROW(configure(config));
ASSERT_NO_THROW(configure(CONFIGS[0]));
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
sol->setRemoteAddr(IOAddress("fe80::abcd"));
......@@ -2707,9 +2734,10 @@ TEST_F(Dhcpv6SrvTest, receiveParseFailedStat) {
EXPECT_EQ(1, recv_drop->getInteger().first);
}
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
/// to call processX() methods.
/// @todo: Implement proper tests for MySQL lease/host database,
/// see ticket #4214.
} // end of anonymous namespace
......@@ -20,6 +20,7 @@
#include <dhcp/hwaddr.h>
#include <dhcpsrv/host.h>
#include <exceptions/exceptions.h>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace dhcp {
......@@ -214,6 +215,9 @@ public:
virtual void rollback() {};
};
/// @brief HostDataSource pointer
typedef boost::shared_ptr<BaseHostDataSource> HostDataSourcePtr;
}
}
......
......@@ -39,9 +39,9 @@ namespace isc {
namespace dhcp {
boost::scoped_ptr<BaseHostDataSource>&
HostDataSourcePtr&
HostDataSourceFactory::getHostDataSourcePtr() {
static boost::scoped_ptr<BaseHostDataSource> hostDataSourcePtr;
static HostDataSourcePtr hostDataSourcePtr;
return (hostDataSourcePtr);
}
......@@ -99,6 +99,7 @@ HostDataSourceFactory::destroy() {
getHostDataSourcePtr().reset();
}
#if 0
BaseHostDataSource&
HostDataSourceFactory::instance() {
BaseHostDataSource* hdsptr = getHostDataSourcePtr().get();
......@@ -108,6 +109,7 @@ HostDataSourceFactory::instance() {
}
return (*hdsptr);
}
#endif
}; // namespace dhcp
}; // namespace isc
......@@ -88,23 +88,12 @@ public:
/// host data source is available.
static void destroy();
/// @brief Return current host data source
///
/// @returns An instance of the "current" host data source. An exception
/// will be thrown if none is available.
///
/// @throw NoHostDataSourceManager No host data source is available: use
/// create() to create one before calling this method.
static BaseHostDataSource& instance();
private:
/// @brief Hold pointer to host data source instance
///
/// Holds a pointer to the singleton host data source. The singleton
/// is encapsulated in this method to avoid a "static initialization
/// fiasco" if defined in an external static variable.
static boost::scoped_ptr<BaseHostDataSource>& getHostDataSourcePtr();
static HostDataSourcePtr& getHostDataSourcePtr();
};
......
......@@ -38,8 +38,6 @@ namespace dhcp {
using namespace isc::asiolink;
boost::shared_ptr<BaseHostDataSource> HostMgr::alternate_source;
boost::scoped_ptr<HostMgr>&
HostMgr::getHostMgrPtr() {
static boost::scoped_ptr<HostMgr> host_mgr_ptr;
......@@ -51,11 +49,20 @@ HostMgr::create(const std::string& access) {
getHostMgrPtr().reset(new HostMgr());
if (!access.empty()) {
// If the user specified parameters, let's pass them to the create
// method. It will destroy any prior instances and will create
// the new one.
HostDataSourceFactory::create(access);
/// @todo Initialize alternate_source here.
//alternate_source = HostDataSourceFactory::getHostDataSourcePtr();
} else {
// Ok, no parameters were specified. We should destroy the existing
// insteance.
HostDataSourceFactory::destroy();
}
// Now store the host data source pointer. It may be NULL. That's ok as
// NULL value indicates that there's no host data source configured.
getHostMgrPtr()->alternate_source_ =
HostDataSourceFactory::getHostDataSourcePtr();
}
HostMgr&
......@@ -70,8 +77,8 @@ HostMgr::instance() {
ConstHostCollection
HostMgr::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
ConstHostCollection hosts = getCfgHosts()->getAll(hwaddr, duid);
if (alternate_source) {
ConstHostCollection hosts_plus = alternate_source->getAll(hwaddr, duid);
if (alternate_source_) {
ConstHostCollection hosts_plus = alternate_source_->getAll(hwaddr, duid);
hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
}
return (hosts);
......@@ -80,8 +87,8 @@ HostMgr::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
ConstHostCollection
HostMgr::getAll4(const IOAddress& address) const {
ConstHostCollection hosts = getCfgHosts()->getAll4(address);
if (alternate_source) {
ConstHostCollection hosts_plus = alternate_source->getAll4(address);
if (alternate_source_) {
ConstHostCollection hosts_plus = alternate_source_->getAll4(address);
hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
}
return (hosts);
......@@ -91,13 +98,13 @@ ConstHostPtr
HostMgr::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
const DuidPtr& duid) const {
ConstHostPtr host = getCfgHosts()->get4(subnet_id, hwaddr, duid);
if (!host && alternate_source) {
if (!host && alternate_source_) {
LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_HWADDR_DUID)
.arg(subnet_id)
.arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
.arg(duid ? duid->toText() : "(duid)");
host = alternate_source->get4(subnet_id, hwaddr, duid);
host = alternate_source_->get4(subnet_id, hwaddr, duid);
}
return (host);
}
......@@ -106,12 +113,12 @@ ConstHostPtr
HostMgr::get4(const SubnetID& subnet_id,
const asiolink::IOAddress& address) const {
ConstHostPtr host = getCfgHosts()->get4(subnet_id, address);
if (!host && alternate_source) {
if (!host && alternate_source_) {
LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_ADDRESS4)
.arg(subnet_id)
.arg(address.toText());
host = alternate_source->get4(subnet_id, address);
host = alternate_source_->get4(subnet_id, address);
}
return (host);
}
......@@ -121,13 +128,13 @@ ConstHostPtr
HostMgr::get6(const SubnetID& subnet_id, const DuidPtr& duid,
const HWAddrPtr& hwaddr) const {
ConstHostPtr host = getCfgHosts()->get6(subnet_id, duid, hwaddr);
if (!host && alternate_source) {
if (!host && alternate_source_) {
LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_DUID_HWADDR)
.arg(subnet_id)
.arg(duid ? duid->toText() : "(duid)")
.arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)");
host = alternate_source->get6(subnet_id, duid, hwaddr);
host = alternate_source_->get6(subnet_id, duid, hwaddr);
}
return (host);
}
......@@ -135,12 +142,12 @@ HostMgr::get6(const SubnetID& subnet_id, const DuidPtr& duid,
ConstHostPtr
HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
ConstHostPtr host = getCfgHosts()->get6(prefix, prefix_len);
if (!host && alternate_source) {
if (!host && alternate_source_) {
LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
HOSTS_MGR_ALTERNATE_GET6_PREFIX)
.arg(prefix.toText())
.arg(static_cast<int>(prefix_len));
host = alternate_source->get6(prefix, prefix_len);
host = alternate_source_->get6(prefix, prefix_len);
}
return (host);
}
......@@ -149,12 +156,12 @@ ConstHostPtr
HostMgr::get6(const SubnetID& subnet_id,
const asiolink::IOAddress& addr) const {
ConstHostPtr host = getCfgHosts()->get6(subnet_id, addr);
if (!host && alternate_source) {
if (!host && alternate_source_) {
LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_ADDRESS6)
.arg(subnet_id)
.arg(addr.toText());
host = alternate_source->get6(subnet_id, addr);
host = alternate_source_->get6(subnet_id, addr);
}
return (host);
}
......
......@@ -207,6 +207,14 @@ public:
return (std::string("host_mgr"));
}
/// @brief Returns pointer to the host data source
///
/// May return NULL
/// @return pointer to the host data source (or NULL)
HostDataSourcePtr getHostDataSource() const {
return (alternate_source_);
}
private:
/// @brief Private default constructor.
......@@ -215,7 +223,7 @@ private:
/// @brief Pointer to an alternate host data source.
///
/// If this pointer is NULL, the source is not in use.
static boost::shared_ptr<BaseHostDataSource> alternate_source;
HostDataSourcePtr alternate_source_;
/// @brief Returns a pointer to the currently used instance of the
/// @c HostMgr.
......
......@@ -19,6 +19,7 @@
#include <boost/scoped_ptr.hpp>
#include <mysql.h>
#include <vector>