summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/dhcp/pkt4.cc3
-rw-r--r--src/lib/dhcpsrv/cfg_subnets4.cc41
-rw-r--r--src/lib/dhcpsrv/cfg_subnets6.cc54
-rw-r--r--src/lib/dhcpsrv/database_backends.dox132
-rw-r--r--src/lib/dhcpsrv/database_connection.cc22
-rw-r--r--src/lib/dhcpsrv/database_connection.h17
-rw-r--r--src/lib/dhcpsrv/db_exceptions.h9
-rw-r--r--src/lib/dhcpsrv/dhcpsrv_messages.mes12
-rw-r--r--src/lib/dhcpsrv/host_mgr.cc10
-rw-r--r--src/lib/dhcpsrv/host_mgr.h2
-rw-r--r--src/lib/dhcpsrv/lease.h2
-rw-r--r--src/lib/dhcpsrv/lease_mgr.cc163
-rw-r--r--src/lib/dhcpsrv/lease_mgr.h142
-rw-r--r--src/lib/dhcpsrv/lease_mgr_factory.cc5
-rw-r--r--src/lib/dhcpsrv/lease_mgr_factory.h5
-rw-r--r--src/lib/dhcpsrv/memfile_lease_mgr.cc277
-rw-r--r--src/lib/dhcpsrv/memfile_lease_mgr.h18
-rw-r--r--src/lib/dhcpsrv/memfile_lease_storage.h2
-rw-r--r--src/lib/dhcpsrv/mysql_connection.cc29
-rw-r--r--src/lib/dhcpsrv/mysql_connection.h14
-rw-r--r--src/lib/dhcpsrv/mysql_host_data_source.cc171
-rw-r--r--src/lib/dhcpsrv/mysql_host_data_source.h3
-rw-r--r--src/lib/dhcpsrv/mysql_lease_mgr.cc173
-rw-r--r--src/lib/dhcpsrv/mysql_lease_mgr.h22
-rw-r--r--src/lib/dhcpsrv/parsers/dbaccess_parser.cc5
-rw-r--r--src/lib/dhcpsrv/pgsql_connection.cc10
-rw-r--r--src/lib/dhcpsrv/pgsql_connection.h15
-rw-r--r--src/lib/dhcpsrv/pgsql_host_data_source.cc196
-rw-r--r--src/lib/dhcpsrv/pgsql_host_data_source.h10
-rw-r--r--src/lib/dhcpsrv/pgsql_lease_mgr.cc136
-rw-r--r--src/lib/dhcpsrv/pgsql_lease_mgr.h22
-rw-r--r--src/lib/dhcpsrv/srv_config.cc17
-rw-r--r--src/lib/dhcpsrv/tests/alloc_engine_utils.cc9
-rw-r--r--src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc1
-rw-r--r--src/lib/dhcpsrv/tests/cfgmgr_unittest.cc20
-rw-r--r--src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc46
-rw-r--r--src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc51
-rw-r--r--src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h17
-rw-r--r--src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc306
-rw-r--r--src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h63
-rw-r--r--src/lib/dhcpsrv/tests/host_mgr_unittest.cc396
-rw-r--r--src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc10
-rw-r--r--src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc16
-rw-r--r--src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc10
-rw-r--r--src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc13
-rw-r--r--src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc10
-rw-r--r--src/lib/dhcpsrv/testutils/Makefile.am10
-rw-r--r--src/lib/dhcpsrv/testutils/schema.cc13
-rw-r--r--src/lib/dhcpsrv/testutils/schema.h8
-rw-r--r--src/lib/eval/eval_context.cc13
-rw-r--r--src/lib/eval/eval_context.h26
-rw-r--r--src/lib/eval/lexer.cc114
-rw-r--r--src/lib/eval/lexer.ll6
-rw-r--r--src/lib/eval/parser.cc619
-rw-r--r--src/lib/eval/parser.h77
-rw-r--r--src/lib/eval/parser.yy20
-rw-r--r--src/lib/eval/tests/context_unittest.cc61
-rw-r--r--src/lib/eval/tests/evaluate_unittest.cc158
-rw-r--r--src/lib/eval/tests/token_unittest.cc70
-rw-r--r--src/lib/eval/token.cc54
-rw-r--r--src/lib/eval/token.h33
-rw-r--r--src/lib/log/logger_level_impl.cc6
-rw-r--r--src/lib/log/logger_level_impl.h6
-rw-r--r--src/lib/log/message_dictionary.cc8
-rw-r--r--src/lib/log/message_dictionary.h4
65 files changed, 3151 insertions, 862 deletions
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 8a2bf7590e..abe71fda1f 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -444,6 +444,9 @@ Pkt4::setHWAddrMember(const uint8_t htype, const uint8_t hlen,
isc_throw(OutOfRange, "Invalid HW Address specified");
}
+ /// @todo: what if mac_addr.size() doesn't match hlen?
+ /// We would happily copy over hardware address that is possibly
+ /// too long or doesn't match hlen value.
hw_addr.reset(new HWAddr(mac_addr, htype));
}
diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc
index 6c6fd408d3..f1c7a8f658 100644
--- a/src/lib/dhcpsrv/cfg_subnets4.cc
+++ b/src/lib/dhcpsrv/cfg_subnets4.cc
@@ -8,6 +8,7 @@
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/addr_utilities.h>
#include <asiolink/io_address.h>
@@ -232,18 +233,21 @@ CfgSubnets4::removeStatistics() {
using namespace isc::stats;
// For each v4 subnet currently configured, remove the statistic.
- /// @todo: May move this to CfgSubnets4 class if there will be more
- /// statistics here.
+ StatsMgr& stats_mgr = StatsMgr::instance();
for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
subnet4 != subnets_.end(); ++subnet4) {
+ SubnetID subnet_id = (*subnet4)->getID();
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "total-addresses"));
- StatsMgr::instance().del(StatsMgr::generateName("subnet",
- (*subnet4)->getID(),
- "total-addresses"));
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "assigned-addresses"));
- StatsMgr::instance().del(StatsMgr::generateName("subnet",
- (*subnet4)->getID(),
- "assigned-addresses"));
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "declined-addresses"));
+
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "declined-reclaimed-addresses"));
}
}
@@ -251,14 +255,21 @@ void
CfgSubnets4::updateStatistics() {
using namespace isc::stats;
- /// @todo: May move this to CfgSubnets4 class if there will be more
- /// statistics here.
- for (Subnet4Collection::const_iterator subnet = subnets_.begin();
- subnet != subnets_.end(); ++subnet) {
+ StatsMgr& stats_mgr = StatsMgr::instance();
+ for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
+ subnet4 != subnets_.end(); ++subnet4) {
+ SubnetID subnet_id = (*subnet4)->getID();
+
+ stats_mgr.setValue(StatsMgr::
+ generateName("subnet", subnet_id, "total-addresses"),
+ static_cast<int64_t>
+ ((*subnet4)->getPoolCapacity(Lease::
+ TYPE_V4)));
+ }
- StatsMgr::instance().setValue(
- StatsMgr::generateName("subnet", (*subnet)->getID(), "total-addresses"),
- static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_V4)));
+ // Only recount the stats if we have subnets.
+ if (subnets_.begin() != subnets_.end()) {
+ LeaseMgrFactory::instance().recountLeaseStats4();
}
}
diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc
index ad8ed4c6c6..3136763f3c 100644
--- a/src/lib/dhcpsrv/cfg_subnets6.cc
+++ b/src/lib/dhcpsrv/cfg_subnets6.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,6 +7,7 @@
#include <config.h>
#include <dhcpsrv/cfg_subnets6.h>
#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet_id.h>
#include <stats/stats_mgr.h>
@@ -176,25 +177,26 @@ void
CfgSubnets6::removeStatistics() {
using namespace isc::stats;
+ StatsMgr& stats_mgr = StatsMgr::instance();
// For each v6 subnet currently configured, remove the statistics.
for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
subnet6 != subnets_.end(); ++subnet6) {
+ SubnetID subnet_id = (*subnet6)->getID();
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-nas"));
- StatsMgr::instance().del(StatsMgr::generateName("subnet",
- (*subnet6)->getID(),
- "total-nas"));
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "assigned-nas"));
- StatsMgr::instance().del(StatsMgr::generateName("subnet",
- (*subnet6)->getID(),
- "assigned-nas"));
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-pds"));
- StatsMgr::instance().del(StatsMgr::generateName("subnet",
- (*subnet6)->getID(),
- "total-pds"));
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "assigned-pds"));
- StatsMgr::instance().del(StatsMgr::generateName("subnet",
- (*subnet6)->getID(),
- "assigned-pds"));
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "declined-addresses"));
+
+ stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
+ "declined-reclaimed-addresses"));
}
}
@@ -202,16 +204,26 @@ void
CfgSubnets6::updateStatistics() {
using namespace isc::stats;
- for (Subnet6Collection::const_iterator subnet = subnets_.begin();
- subnet != subnets_.end(); ++subnet) {
+ StatsMgr& stats_mgr = StatsMgr::instance();
+ // For each v6 subnet currently configured, calculate totals
+ for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
+ subnet6 != subnets_.end(); ++subnet6) {
+ SubnetID subnet_id = (*subnet6)->getID();
+
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "total-nas"),
+ static_cast<int64_t>
+ ((*subnet6)->getPoolCapacity(Lease::TYPE_NA)));
- StatsMgr::instance().setValue(
- StatsMgr::generateName("subnet", (*subnet)->getID(), "total-nas"),
- static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_NA)));
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "total-pds"),
+ static_cast<int64_t>
+ ((*subnet6)->getPoolCapacity(Lease::TYPE_PD)));
+ }
- StatsMgr::instance().setValue(
- StatsMgr::generateName("subnet", (*subnet)->getID(), "total-pds"),
- static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_PD)));
+ // Only recount the stats if we have subnets.
+ if (subnets_.begin() != subnets_.end()) {
+ LeaseMgrFactory::instance().recountLeaseStats6();
}
}
diff --git a/src/lib/dhcpsrv/database_backends.dox b/src/lib/dhcpsrv/database_backends.dox
index 104318ece2..682bc5dea0 100644
--- a/src/lib/dhcpsrv/database_backends.dox
+++ b/src/lib/dhcpsrv/database_backends.dox
@@ -83,137 +83,5 @@
- <b>user</b> - database user ID under which the database is accessed. If not
specified, no user ID is used - the database is assumed to be open.
- @section dhcp-backend-unittest Running Unit Tests
-
- With the use of databases requiring separate authorisation, there are
- certain database-specific pre-requisites for successfully running the unit
- tests. These are listed in the following sections.
-
- @subsection dhcp-mysql-unittest MySQL Unit Tests
-
- A database called <i>keatest</i> must be created. A database user, also called
- <i>keatest</i> (and with a password <i>keatest</i>) must also be created and
- be given full privileges in that database. The unit tests create the schema
- in the database before each test and delete it afterwards.
-
- In detail, the steps to create the database and user are:
-
- -# Log into MySQL as root:
- @verbatim
- % mysql -u root -p
- Enter password:
- :
- mysql>@endverbatim\n
- -# Create the test database. This must be called "keatest":
- @verbatim
- mysql> CREATE DATABASE keatest;
- mysql>@endverbatim\n
- -# Create the user under which the test client will connect to the database
- (the apostrophes around the words <i>keatest</i> and <i>localhost</i> are
- required):
- @verbatim
- mysql> CREATE USER 'keatest'@'localhost' IDENTIFIED BY 'keatest';
- mysql>@endverbatim\n
- -# Grant the created user permissions to access the <i>keatest</i> database
- (again, the apostrophes around the words <i>keatest</i> and <i>localhost</i>
- are required):
- @verbatim
- mysql> GRANT ALL ON keatest.* TO 'keatest'@'localhost';
- mysql>@endverbatim\n
- -# Exit MySQL:
- @verbatim
- mysql> quit
- Bye
- %@endverbatim
-
- The unit tests are run automatically when "make check" is executed (providing
- that Kea has been build with the \--with-dhcp-mysql switch (see the installation
- section in the <a href="http://kea.isc.org/docs/kea-guide.html">Kea Administrator
- Reference Manual</a>).
-
- @subsection dhcp-pgsql-unittest PostgreSQL Unit Tests
-
- Conceptually, the steps required to run PostgreSQL unit-tests are the same as
- in MySQL. First, a database called <i>keatest</i> must be created. A database
- user, also called <i>keatest</i> (that will be allowed to log in using password
- <i>keatest</i>) must be created and given full privileges in that database. The
- unit tests create the schema in the database before each test and delete it
- afterwards.
-
- PostgreSQL set up differs from system to system. Please consult your OS-specific
- PostgreSQL documentation. The remainder of that section uses Ubuntu 13.10 x64 as
- example. On Ubuntu, after installing PostgreSQL (with <tt>sudo apt-get install
- postgresql</tt>), it is installed as user <i>postgres</i>. To create new databases
- or add new users, initial commands must be issued as user postgres:
-
-@verbatim
-$ sudo -u postgres psql postgres
-[sudo] password for thomson:
-psql (9.1.12)
-Type "help" for help.
-postgres=# CREATE USER keatest WITH PASSWORD 'keatest';
-CREATE ROLE
-postgres=# CREATE DATABASE keatest;
-CREATE DATABASE
-postgres=# GRANT ALL PRIVILEGES ON DATABASE keatest TO keatest;
-GRANT
-postgres=# \q
-@endverbatim
-
- Now we are back to our regular, unprivileged user. Try to log into the newly
- created database using keatest credentials:
-@verbatim
-$ psql -d keatest -U keatest
-Password for user keatest:
-psql (9.1.12)
-Type "help" for help.
-
-keatest=>
-@endverbatim
-
- If instead of seeing keatest=> prompt, your login will be refused with error
- code about failed peer or indent authentication, it means that PostgreSQL is
- configured to check unix username and reject login attepts if PostgreSQL names
- are different. To alter that, PostgreSQL configuration must be changed.
- Alternatively, you may set up your environment, so the tests would be run from
- unix account keatest. <tt>/etc/postgresql/9.1/main/pg_hba.conf</tt> config file
- had to betweaked. It may be in a different location in your system. The following
- lines:
-
-@verbatim
-local all all peer
-host all all 127.0.0.1/32 md5
-host all all ::1/128 md5
-@endverbatim
-
- were replaced with:
-
-@verbatim
-local all all password
-host all all 127.0.0.1/32 password
-host all all ::1/128 password
-@endverbatim
-
- Another possible problem is to get no password prompt, in general because
- you have no <tt>pg_hba.conf</tt> config file and everybody is by default
- trusted. As it has a very bad effect on the security you should have
- been warned it is a highly unsafe config. The solution is the same,
- i.e., require password or md5 authentication method. If you lose
- the postgres user access you can add first:
-@verbatim
-local all postgres trust
-@endverbatim
- to trust only the local postgres user. Note the postgres user can
- be pgsql on some systems.
-
- Please consult your PostgreSQL user manual before applying those changes as
- those changes may expose your other databases that you run on the same system.
- In general case, it is a poor idea to run anything of value on a system
- that runs tests. Use caution!
-
- The unit tests are run automatically when "make check" is executed (providing
- that Kea has been build with the \--with-dhcp-pgsql switch (see the installation
- section in the <a href="http://kea.isc.org/docs/kea-guide.html">Kea Administrator
- Reference Manual</a>).
*/
diff --git a/src/lib/dhcpsrv/database_connection.cc b/src/lib/dhcpsrv/database_connection.cc
index 983a752d96..0aa886fa88 100644
--- a/src/lib/dhcpsrv/database_connection.cc
+++ b/src/lib/dhcpsrv/database_connection.cc
@@ -1,10 +1,11 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <dhcpsrv/database_connection.h>
+#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <exceptions/exceptions.h>
@@ -86,5 +87,24 @@ DatabaseConnection::redactedAccessString(const ParameterMap& parameters) {
return (access);
}
+bool
+DatabaseConnection::configuredReadOnly() const {
+ std::string readonly_value = "false";
+ try {
+ readonly_value = getParameter("readonly");
+ boost::algorithm::to_lower(readonly_value);
+ } catch (...) {
+ // Parameter "readonly" hasn't been specified so we simply use
+ // the default value of "false".
+ }
+
+ if ((readonly_value != "false") && (readonly_value != "true")) {
+ isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value
+ << "' specified for boolean parameter 'readonly'");
+ }
+
+ return (readonly_value == "true");
+}
+
};
};
diff --git a/src/lib/dhcpsrv/database_connection.h b/src/lib/dhcpsrv/database_connection.h
index 2be98baf41..e6a902babf 100644
--- a/src/lib/dhcpsrv/database_connection.h
+++ b/src/lib/dhcpsrv/database_connection.h
@@ -54,6 +54,15 @@ public:
isc::Exception(file, line, what) {}
};
+/// @brief Invalid 'readonly' value specification.
+///
+/// Thrown when the value of the 'readonly' boolean parameter is invalid.
+class DbInvalidReadOnly : public Exception {
+public:
+ DbInvalidReadOnly(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
/// @brief Common database connection class.
///
@@ -112,6 +121,14 @@ public:
/// @return Redacted database access string.
static std::string redactedAccessString(const ParameterMap& parameters);
+ /// @brief Convenience method checking if database should be opened with
+ /// read only access.
+ ///
+ /// @return true if "readonly" parameter is specified and set to true;
+ /// false if "readonly" parameter is not specified or it is specified
+ /// and set to false.
+ bool configuredReadOnly() const;
+
private:
/// @brief List of parameters passed in dbconfig
diff --git a/src/lib/dhcpsrv/db_exceptions.h b/src/lib/dhcpsrv/db_exceptions.h
index 664c43e144..d9cc4449dd 100644
--- a/src/lib/dhcpsrv/db_exceptions.h
+++ b/src/lib/dhcpsrv/db_exceptions.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -40,6 +40,13 @@ public:
isc::Exception(file, line, what) {}
};
+/// @brief Attempt to modify data in read-only database.
+class ReadOnlyDb : public Exception {
+public:
+ ReadOnlyDb(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
};
};
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index 714148548c..e9ff369859 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -551,6 +551,12 @@ connection including database name and username needed to access it
A debug message issued when the server is about to obtain schema version
information from the MySQL hosts database.
+% DHCPSRV_MYSQL_HOST_DB_READONLY MySQL host database opened for read access only
+This informational message is issued when the user has configured the MySQL
+database in read-only mode. Kea will not be able to insert or modify
+host reservations but will be able to retrieve existing ones and
+assign them to the clients communicating with the server.
+
% DHCPSRV_MYSQL_ROLLBACK rolling back MySQL database
The code has issued a rollback call. All outstanding transaction will
be rolled back and not committed to the database.
@@ -696,6 +702,12 @@ V6) is about to open a PostgreSQL hosts database. The parameters of the
connection including database name and username needed to access it
(but not the password if any) are logged.
+% DHCPSRV_PGSQL_HOST_DB_READONLY PostgreSQL host database opened for read access only
+This informational message is issued when the user has configured the PostgreSQL
+database in read-only mode. Kea will not be able to insert or modify
+host reservations but will be able to retrieve existing ones and
+assign them to the clients communicating with the server.
+
% DHCPSRV_PGSQL_ROLLBACK rolling back PostgreSQL database
The code has issued a rollback call. All outstanding transaction will
be rolled back and not committed to the database.
diff --git a/src/lib/dhcpsrv/host_mgr.cc b/src/lib/dhcpsrv/host_mgr.cc
index 16709c5be8..090a5433ac 100644
--- a/src/lib/dhcpsrv/host_mgr.cc
+++ b/src/lib/dhcpsrv/host_mgr.cc
@@ -47,7 +47,7 @@ HostMgr::create(const std::string& access) {
HostDataSourceFactory::create(access);
} else {
// Ok, no parameters were specified. We should destroy the existing
- // insteance.
+ // instance.
HostDataSourceFactory::destroy();
}
@@ -214,8 +214,12 @@ HostMgr::get6(const SubnetID& subnet_id,
}
void
-HostMgr::add(const HostPtr&) {
- isc_throw(isc::NotImplemented, "HostMgr::add is not implemented");
+HostMgr::add(const HostPtr& host) {
+ if (!alternate_source_) {
+ isc_throw(NoHostDataSourceManager, "unable to add new host because there is "
+ "no alternate host data source present");
+ }
+ alternate_source_->add(host);
}
} // end of isc::dhcp namespace
diff --git a/src/lib/dhcpsrv/host_mgr.h b/src/lib/dhcpsrv/host_mgr.h
index f4633281b9..f7483fe074 100644
--- a/src/lib/dhcpsrv/host_mgr.h
+++ b/src/lib/dhcpsrv/host_mgr.h
@@ -53,7 +53,7 @@ namespace dhcp {
/// reservations specified in the configuration file) can't be disabled.
///
/// @todo Implement alternate host data sources: MySQL, PostgreSQL, etc.
-class HostMgr : public boost::noncopyable, BaseHostDataSource {
+class HostMgr : public boost::noncopyable, public BaseHostDataSource {
public:
/// @brief Creates new instance of the @c HostMgr.
diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h
index d1ab300f11..c89ad523a3 100644
--- a/src/lib/dhcpsrv/lease.h
+++ b/src/lib/dhcpsrv/lease.h
@@ -52,8 +52,6 @@ struct Lease {
/// @brief Expired and reclaimed lease.
static const uint32_t STATE_EXPIRED_RECLAIMED;
- //@}
-
/// @brief Returns name(s) of the basic lease state(s).
///
/// @param state A numeric value holding a state information.
diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc
index 58f95d7dc9..cd84a69839 100644
--- a/src/lib/dhcpsrv/lease_mgr.cc
+++ b/src/lib/dhcpsrv/lease_mgr.cc
@@ -6,8 +6,11 @@
#include <config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/lease_mgr.h>
#include <exceptions/exceptions.h>
+#include <stats/stats_mgr.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
@@ -44,6 +47,166 @@ LeaseMgr::getLease6(Lease::Type type, const DUID& duid,
return (*col.begin());
}
+void
+LeaseMgr::recountLeaseStats4() {
+ using namespace stats;
+
+ StatsMgr& stats_mgr = StatsMgr::instance();
+
+ LeaseStatsQueryPtr query = startLeaseStatsQuery4();
+ if (!query) {
+ /// NULL means not backend does not support recounting.
+ return;
+ }
+
+ // Zero out the global stats.
+ int64_t zero = 0;
+ stats_mgr.setValue("declined-addresses", zero);
+ stats_mgr.setValue("declined-reclaimed-addresses", zero);
+
+ // Clear subnet level stats. This ensures we don't end up with corner
+ // cases that leave stale values in place.
+ const Subnet4Collection* subnets =
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
+
+ for (Subnet4Collection::const_iterator subnet = subnets->begin();
+ subnet != subnets->end(); ++subnet) {
+ SubnetID subnet_id = (*subnet)->getID();
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "assigned-addresses"),
+ zero);
+
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "declined-addresses"),
+ zero);
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "declined-reclaimed-addresses"),
+ zero);
+ }
+
+ // Get counts per state per subnet. Iterate over the result set
+ // updating the subnet and global values.
+ LeaseStatsRow row;
+ while (query->getNextRow(row)) {
+ if (row.lease_state_ == Lease::STATE_DEFAULT) {
+ // Set subnet level value.
+ stats_mgr.setValue(StatsMgr::generateName("subnet", row.subnet_id_,
+ "assigned-addresses"),
+ row.state_count_);
+ } else if (row.lease_state_ == Lease::STATE_DECLINED) {
+ // Set subnet level value.
+ stats_mgr.setValue(StatsMgr::generateName("subnet", row.subnet_id_,
+ "declined-addresses"),
+ row.state_count_);
+
+ // Add to the global value.
+ stats_mgr.addValue("declined-addresses", row.state_count_);
+ }
+ }
+}
+
+LeaseStatsQueryPtr
+LeaseMgr::startLeaseStatsQuery4() {
+ return(LeaseStatsQueryPtr());
+}
+
+bool
+LeaseStatsQuery::getNextRow(LeaseStatsRow& /*row*/) {
+ return (false);
+}
+
+void
+LeaseMgr::recountLeaseStats6() {
+ using namespace stats;
+
+ StatsMgr& stats_mgr = StatsMgr::instance();
+
+ LeaseStatsQueryPtr query = startLeaseStatsQuery6();
+ if (!query) {
+ /// NULL means not backend does not support recounting.
+ return;
+ }
+
+ // Zero out the global stats. (Ok, so currently there's only one
+ // that should be cleared. "reclaimed-declined-addresses" never
+ // gets zeroed. @todo discuss with Tomek the rational of not
+ // clearing it when we clear the rest.
+ int64_t zero = 0;
+ stats_mgr.setValue("declined-addresses", zero);
+ stats_mgr.setValue("declined-reclaimed-addresses", zero);
+
+ // Clear subnet level stats. This ensures we don't end up with corner
+ // cases that leave stale values in place.
+ const Subnet6Collection* subnets =
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
+
+ for (Subnet6Collection::const_iterator subnet = subnets->begin();
+ subnet != subnets->end(); ++subnet) {
+ SubnetID subnet_id = (*subnet)->getID();
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "assigned-nas"),
+ zero);
+
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "declined-addresses"),
+ zero);
+
+ stats_mgr.setValue(StatsMgr::
+ generateName("subnet", subnet_id,
+ "declined-reclaimed-addresses"),
+ zero);
+
+ stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
+ "assigned-pds"),
+ zero);
+ }
+
+ // Get counts per state per subnet. Iterate over the result set
+ // updating the subnet and global values.
+ LeaseStatsRow row;
+ while (query->getNextRow(row)) {
+ switch(row.lease_type_) {
+ case Lease::TYPE_NA:
+ if (row.lease_state_ == Lease::STATE_DEFAULT) {
+ // Set subnet level value.
+ stats_mgr.setValue(StatsMgr::
+ generateName("subnet", row.subnet_id_,
+ "assigned-nas"),
+ row.state_count_);
+ } if (row.lease_state_ == Lease::STATE_DECLINED) {
+ // Set subnet level value.
+ stats_mgr.setValue(StatsMgr::
+ generateName("subnet", row.subnet_id_,
+ "declined-addresses"),
+ row.state_count_);
+
+ // Add to the global value.
+ stats_mgr.addValue("declined-addresses", row.state_count_);
+ }
+ break;
+
+ case Lease::TYPE_PD:
+ if (row.lease_state_ == Lease::STATE_DEFAULT) {
+ // Set subnet level value.
+ stats_mgr.setValue(StatsMgr::
+ generateName("subnet", row.subnet_id_,
+ "assigned-pds"),
+ row.state_count_);
+ }
+ break;
+
+ default:
+ // We dont' support TYPE_TAs yet
+ break;
+ }
+ }
+}
+
+LeaseStatsQueryPtr
+LeaseMgr::startLeaseStatsQuery6() {
+ return(LeaseStatsQueryPtr());
+}
+
std::string
LeaseMgr::getDBVersion() {
isc_throw(NotImplemented, "LeaseMgr::getDBVersion() called");
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index c31f2dfa12..c8b4ef4e0d 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -146,6 +146,86 @@ public:
virtual ~SqlExchange() {};
ExchangeColumnInfoContainer parameters_; ///< Column names and types
};
+
+/// @brief Contains a single row of lease statistical data
+///
+/// The contents of the row consist of a subnet ID, a lease
+/// type, a lease state, and the number of leases in that state
+/// for that type for that subnet ID.
+struct LeaseStatsRow {
+ /// @brief Default constructor
+ LeaseStatsRow() :
+ subnet_id_(0), lease_type_(Lease::TYPE_NA),
+ lease_state_(Lease::STATE_DEFAULT), state_count_(0) {
+ }
+
+ /// @brief Constructor
+ ///
+ /// Constructor which defaults the type to TYPE_NA.
+ ///
+ /// @param subnet_id The subnet id to which this data applies
+ /// @param lease_state The lease state counted
+ /// @param state_count The count of leases in the lease state
+ LeaseStatsRow(const SubnetID& subnet_id, const uint32_t lease_state,
+ const int64_t state_count)
+ : subnet_id_(subnet_id), lease_type_(Lease::TYPE_NA),
+ lease_state_(lease_state), state_count_(state_count) {
+ }
+
+ /// @brief Constructor
+ ///
+ /// @param subnet_id The subnet id to which this data applies
+ /// @param lease_type The lease type for this state count
+ /// @param lease_state The lease state counted
+ /// @param state_count The count of leases in the lease state
+ LeaseStatsRow(const SubnetID& subnet_id, const Lease::Type& lease_type,
+ const uint32_t lease_state, const int64_t state_count)
+ : subnet_id_(subnet_id), lease_type_(lease_type),
+ lease_state_(lease_state), state_count_(state_count) {
+ }
+
+ /// @brief The subnet ID to which this data applies
+ SubnetID subnet_id_;
+ /// @brief The lease_type to which the count applies
+ Lease::Type lease_type_;
+ /// @brief The lease_state to which the count applies
+ uint32_t lease_state_;
+ /// @brief state_count The count of leases in the lease state
+ int64_t state_count_;
+};
+
+/// @brief Base class for fulfilling a statistical lease data query
+///
+/// LeaseMgr derivations implement this class such that it provides
+/// upto date statistical lease data organized as rows of LeaseStatsRow
+/// instances. The rows must be accessible in ascending order by subnet id.
+class LeaseStatsQuery {
+public:
+ /// @brief Default constructor
+ LeaseStatsQuery() {};
+
+ /// @brief virtual destructor
+ virtual ~LeaseStatsQuery() {};
+
+ /// @brief Executes the query
+ ///
+ /// This method should conduct whatever steps are required to
+ /// calculate the lease statistical data by examining the
+ /// lease data and making that results available row by row.
+ virtual void start() {};
+
+ /// @brief Fetches the next row of data
+ ///
+ /// @param[out] row Storage into which the row is fetched
+ ///
+ /// @return True if a row was fetched, false if there are no
+ /// more rows.
+ virtual bool getNextRow(LeaseStatsRow& row);
+};
+
+/// @brief Defines a pointer to an LeaseStatsQuery.
+typedef boost::shared_ptr<LeaseStatsQuery> LeaseStatsQueryPtr;
+
/// @brief Abstract Lease Manager
///
/// This is an abstract API for lease database backends. It provides unified
@@ -397,6 +477,68 @@ public:
/// @return Number of leases deleted.
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs) = 0;
+ /// @brief Recalculates per-subnet and global stats for IPv4 leases
+ ///
+ /// This method recalculates the following statistics:
+ /// per-subnet:
+ /// - assigned-addresses
+ /// - declined-addresses
+ /// - declined-reclaimed-addresses (reset to zero)
+ /// global:
+ /// - declined-addresses
+ /// - declined-reclaimed-addresses (reset to zero)
+ ///
+ /// It invokes the virtual method, startLeaseStatsQuery4(), which
+ /// returns an instance of an LeaseStatsQuery. The query
+ /// query contains a "result set" where each row is an LeaseStatRow
+ /// that contains a subnet id, a lease type (currently always TYPE_NA),
+ /// a lease state, and the number of leases of that type, in that state
+ /// and is ordered by subnet id. The method iterates over the
+ /// result set rows, setting the appropriate statistic per subnet and
+ /// adding to the approporate global statistic.
+ void recountLeaseStats4();
+
+ /// @brief Virtual method which creates and runs the IPv4 lease stats query
+ ///
+ /// LeaseMgr derivations implement this method such that it creates and
+ /// returns an instance of an LeaseStatsQuery whose result set has been
+ /// populated with upto date IPv4 lease statistical data. Each row of the
+ /// result set is an LeaseStatRow which ordered ascending by subnet ID.
+ ///
+ /// @return A populated LeaseStatsQuery
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery4();
+
+ /// @brief Recalculates per-subnet and global stats for IPv6 leases
+ ///
+ /// This method recalculates the following statistics:
+ /// per-subnet:
+ /// - assigned-addresses
+ /// - declined-addresses
+ /// - declined-reclaimed-addresses (reset to zero)
+ /// - assigned-pds
+ /// global:
+ /// - declined-addresses
+ /// - declined-reclaimed-addresses (reset to zero)
+ ///
+ /// It invokes the virtual method, startLeaseStatsQuery6(), which
+ /// returns an instance of an LeaseStatsQuery. The query contains
+ /// a "result set" where each row is an LeaseStatRow that contains
+ /// a subnet id, a lease type, a lease state, and the number of leases
+ /// of that type, in that state and is ordered by subnet id. The method
+ /// iterates over the result set rows, setting the appropriate statistic
+ /// per subnet and adding to the approporate global statistic.
+ void recountLeaseStats6();
+
+ /// @brief Virtual method which creates and runs the IPv6 lease stats query
+ ///
+ /// LeaseMgr derivations implement this method such that it creates and
+ /// returns an instance of an LeaseStatsQuery whose result set has been
+ /// populated with upto date IPv6 lease statistical data. Each row of the
+ /// result set is an LeaseStatRow which ordered ascending by subnet ID.
+ ///
+ /// @return A populated LeaseStatsQuery
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery6();
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
diff --git a/src/lib/dhcpsrv/lease_mgr_factory.cc b/src/lib/dhcpsrv/lease_mgr_factory.cc
index 5f2f33a4f3..a9f1507b2b 100644
--- a/src/lib/dhcpsrv/lease_mgr_factory.cc
+++ b/src/lib/dhcpsrv/lease_mgr_factory.cc
@@ -101,6 +101,11 @@ LeaseMgrFactory::destroy() {
getLeaseMgrPtr().reset();
}
+bool
+LeaseMgrFactory::haveInstance() {
+ return (getLeaseMgrPtr().get());
+}
+
LeaseMgr&
LeaseMgrFactory::instance() {
LeaseMgr* lmptr = getLeaseMgrPtr().get();
diff --git a/src/lib/dhcpsrv/lease_mgr_factory.h b/src/lib/dhcpsrv/lease_mgr_factory.h
index 279058bed8..4c6baf1270 100644
--- a/src/lib/dhcpsrv/lease_mgr_factory.h
+++ b/src/lib/dhcpsrv/lease_mgr_factory.h
@@ -85,7 +85,10 @@ public:
/// create() to create one before calling this method.
static LeaseMgr& instance();
-
+ /// @brief Indicates if the lease manager has been instantiated.
+ ///
+ /// @return True if the lease manager instance exists, false otherwise.
+ static bool haveInstance();
private:
/// @brief Hold pointer to lease manager
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc
index 51314b4cd8..4ff78be9d1 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.cc
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc
@@ -256,6 +256,268 @@ LFCSetup::getExitStatus() const {
return (process_->getExitStatus(pid_));
}
+
+/// @brief Base Memfile derivation of the statistical lease data query
+///
+/// This class provides the functionality such as results storage and row
+/// fetching common to fulfilling the statistical lease data query.
+///
+class MemfileLeaseStatsQuery : public LeaseStatsQuery {
+public:
+ /// @brief Constructor
+ ///
+ MemfileLeaseStatsQuery()
+ : rows_(0), next_pos_(rows_.end()) {
+ };
+
+ /// @brief Destructor
+ virtual ~MemfileLeaseStatsQuery() {};
+
+ /// @brief Fetches the next row in the result set
+ ///
+ /// Once the internal result set has been populated by invoking the
+ /// the start() method, this method is used to iterate over the
+ /// result set rows. Once the last row has been fetched, subsequent
+ /// calls will return false.
+ /// @param row Storage for the fetched row
+ ///
+ /// @return True if the fetch succeeded, false if there are no more
+ /// rows to fetch.
+ virtual bool getNextRow(LeaseStatsRow& row) {
+ if (next_pos_ == rows_.end()) {
+ return (false);
+ }
+
+ row = *next_pos_;
+ ++next_pos_;
+ return (true);
+ }
+
+ /// @brief Returns the number of rows in the result set
+ int getRowCount() const {
+ return (rows_.size());
+ }
+
+protected:
+ /// @brief A vector containing the "result set"
+ std::vector<LeaseStatsRow> rows_;
+
+ /// @brief An iterator for accessing the next row within the result set
+ std::vector<LeaseStatsRow>::iterator next_pos_;
+};
+
+/// @brief Memfile derivation of the IPv4 statistical lease data query
+///
+/// This class is used to recalculate IPv4 lease statistics for Memfile
+/// lease storage. It does so by iterating over the given storage,
+/// accumulating counts of leases in each of the monitored lease states
+/// for each subnet and storing these counts in an internal collection.
+/// The populated result set will contain one entry per monitored state
+/// per subnet.
+///
+class MemfileLeaseStatsQuery4 : public MemfileLeaseStatsQuery {
+public:
+ /// @brief Constructor
+ ///
+ /// @param storage4 A pointer to the v4 lease storage to be counted
+ MemfileLeaseStatsQuery4(Lease4Storage& storage4)
+ : MemfileLeaseStatsQuery(), storage4_(storage4) {
+ };
+
+ /// @brief Destructor
+ virtual ~MemfileLeaseStatsQuery4() {};
+
+ /// @brief Creates the IPv4 lease statistical data result set
+ ///
+ /// The result set is populated by iterating over the IPv4 leases in
+ /// storage, in ascending order by address, accumulating the lease state
+ /// counts per subnet. Note that walking the leases by address should
+ /// inherently group them by subnet, and while this does not gaurantee
+ /// ascending order of subnet id, it should be sufficient to accumulate
+ /// state counts per subnet. This avoids introducing an additional
+ /// subnet_id index.
+ /// At the completion of all entries for a given subnet, the counts are
+ /// used to create LeaseStatsRow instances which are appended to an
+ /// internal vector. The process results in a vector containing one entry
+ /// per state per subnet.
+ ///
+ /// Currently the states counted are:
+ ///
+ /// - Lease::STATE_DEFAULT (i.e. assigned)
+ /// - Lease::STATE_DECLINED
+ void start() {
+ const Lease4StorageAddressIndex& idx
+ = storage4_.get<AddressIndexTag>();
+
+ // Iterate over the leases in order by subnet, accumulating per
+ // subnet counts for each state of interest. As we finish each
+ // subnet, add the appropriate rows to our result set.
+ SubnetID cur_id = 0;
+ int64_t assigned = 0;
+ int64_t declined = 0;
+ for(Lease4StorageAddressIndex::const_iterator lease = idx.begin();
+ lease != idx.end(); ++lease) {
+ // If we've hit the next subnet, add rows for the current subnet
+ // and wipe the accumulators
+ if ((*lease)->subnet_id_ != cur_id) {
+ if (cur_id > 0) {
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DEFAULT,
+ assigned));
+ assigned = 0;
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DECLINED,
+ declined));
+ declined = 0;
+ }
+
+ // Update current subnet id
+ cur_id = (*lease)->subnet_id_;
+ }
+
+ // Bump the appropriate accumulator
+ if ((*lease)->state_ == Lease::STATE_DEFAULT) {
+ ++assigned;
+ } else if ((*lease)->state_ == Lease::STATE_DECLINED) {
+ ++declined;
+ }
+ }
+
+ // Make the rows for last subnet, unless there were no rows
+ if (idx.begin() != idx.end()) {
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DEFAULT,
+ assigned));
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DECLINED,
+ declined));
+ }
+
+ // Set the next row position to the beginning of the rows.
+ next_pos_ = rows_.begin();
+ }
+
+private:
+ /// @brief The Memfile storage containing the IPv4 leases to analyze
+ Lease4Storage& storage4_;
+};
+
+
+/// @brief Memfile derivation of the IPv6 statistical lease data query
+///
+/// This class is used to recalculate IPv6 lease statistics for Memfile
+/// lease storage. It does so by iterating over the given storage,
+/// accumulating counts of leases in each of the monitored lease states
+/// for each subnet and storing these counts in an internal collection.
+/// The populated result set will contain one entry per monitored state
+/// per subnet.
+///
+class MemfileLeaseStatsQuery6 : public MemfileLeaseStatsQuery {
+public:
+ /// @brief Constructor
+ ///
+ /// @param storage6 A pointer to the v6 lease storage to be counted
+ MemfileLeaseStatsQuery6(Lease6Storage& storage6)
+ : MemfileLeaseStatsQuery(), storage6_(storage6) {
+ };
+
+ /// @brief Destructor
+ virtual ~MemfileLeaseStatsQuery6() {};
+
+ /// @brief Creates the IPv6 lease statistical data result set
+ ///
+ /// The result set is populated by iterating over the IPv6 leases in
+ /// storage, in ascending order by address, accumulating the lease state
+ /// counts per subnet. Note that walking the leases by address should
+ /// inherently group them by subnet, and while this does not gaurantee
+ /// ascending order of subnet id, it should be sufficient to accumulate
+ /// state counts per subnet. This avoids introducing an additional
+ /// subnet_id index.
+ /// At the completion of all entries for a given subnet, the counts
+ /// are used to create LeaseStatsRow instances which are appended to an
+ /// internal vector. The process results in a vector containing one entry
+ /// per state per lease type per subnet.
+ ///
+ /// Currently the states counted are:
+ ///
+ /// - Lease::STATE_DEFAULT (i.e. assigned)
+ /// - Lease::STATE_DECLINED
+ virtual void start() {
+ // Get the subnet_id index
+ const Lease6StorageAddressIndex& idx
+ = storage6_.get<AddressIndexTag>();
+
+ // Iterate over the leases in order by subnet, accumulating per
+ // subnet counts for each state of interest. As we finish each
+ // subnet, add the appropriate rows to our result set.
+ SubnetID cur_id = 0;
+ int64_t assigned = 0;
+ int64_t declined = 0;
+ int64_t assigned_pds = 0;
+
+ for(Lease6StorageAddressIndex::const_iterator lease = idx.begin();
+ lease != idx.end(); ++lease) {
+
+ // If we've hit the next subnet, add rows for the current subnet
+ // and wipe the accumulators
+ if ((*lease)->subnet_id_ != cur_id) {
+ if (cur_id > 0) {
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA,
+ Lease::STATE_DEFAULT,
+ assigned));
+ assigned = 0;
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA,
+ Lease::STATE_DECLINED,
+ declined));
+ declined = 0;
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_PD,
+ Lease::STATE_DEFAULT,
+ assigned_pds));
+ assigned_pds = 0;
+ }
+
+ // Update current subnet id
+ cur_id = (*lease)->subnet_id_;
+ }
+
+ // Bump the appropriate accumulator
+ if ((*lease)->state_ == Lease::STATE_DEFAULT) {
+ switch((*lease)->type_) {
+ case Lease::TYPE_NA:
+ ++assigned;
+ break;
+ case Lease::TYPE_PD:
+ ++assigned_pds;
+ break;
+ default:
+ break;
+ }
+ } else if ((*lease)->state_ == Lease::STATE_DECLINED) {
+ // In theory only NAs can be declined
+ if (((*lease)->type_) == Lease::TYPE_NA) {
+ ++declined;
+ }
+ }
+ }
+
+ // Make the rows for last subnet, unless there were no rows
+ if (idx.begin() != idx.end()) {
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA,
+ Lease::STATE_DEFAULT,
+ assigned));
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA,
+ Lease::STATE_DECLINED,
+ declined));
+ rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_PD,
+ Lease::STATE_DEFAULT,
+ assigned_pds));
+ }
+
+ // Set the next row position to the beginning of the rows.
+ next_pos_ = rows_.begin();
+ }
+
+private:
+ /// @brief The Memfile storage containing the IPv6 leases to analyze
+ Lease6Storage& storage6_;
+};
+
// Explicit definition of class static constants. Values are given in the
// declaration so they're not needed here.
const int Memfile_LeaseMgr::MAJOR_VERSION;
@@ -299,6 +561,7 @@ Memfile_LeaseMgr::Memfile_LeaseMgr(const DatabaseConnection::ParameterMap& param
}
lfcSetup(conversion_needed);
}
+
}
Memfile_LeaseMgr::~Memfile_LeaseMgr() {
@@ -1048,5 +1311,19 @@ void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file)
}
}
+LeaseStatsQueryPtr
+Memfile_LeaseMgr::startLeaseStatsQuery4() {
+ LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery4(storage4_));
+ query->start();
+ return(query);
+}
+
+LeaseStatsQueryPtr
+Memfile_LeaseMgr::startLeaseStatsQuery6() {
+ LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery6(storage6_));
+ query->start();
+ return(query);
+}
+
} // end of namespace isc::dhcp
} // end of namespace isc
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h
index 1ace364cf7..bdbd985511 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.h
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.h
@@ -92,6 +92,7 @@ public:
/// @}
+
/// @brief Specifies universe (V4, V6)
///
/// This enumeration is used by various functions in Memfile %Lease Manager,
@@ -594,6 +595,23 @@ public:
int getLFCExitStatus() const;
//@}
+ /// @brief Creates and runs the IPv4 lease stats query
+ ///
+ /// It creates an instance of a MemfileLeaseStatsQuery4 and then
+ /// invokes its start method in which the query constructs its
+ /// statistical data result set. The query object is then returned.
+ ///
+ /// @return The populated query as a pointer to an LeaseStatsQuery
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery4();
+
+ /// @brief Creates and runs the IPv6 lease stats query
+ ///
+ /// It creates an instance of a MemfileLeaseStatsQuery6 and then
+ /// invokes its start method in which the query constructs its
+ /// statistical data result set. The query object is then returned.
+ ///
+ /// @return The populated query as a pointer to an LeaseStatsQuery.
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery6();
/// @name Protected methods used for %Lease File Cleanup.
/// The following methods are protected so as they can be accessed and
diff --git a/src/lib/dhcpsrv/memfile_lease_storage.h b/src/lib/dhcpsrv/memfile_lease_storage.h
index 566a74d7c5..3b07fad238 100644
--- a/src/lib/dhcpsrv/memfile_lease_storage.h
+++ b/src/lib/dhcpsrv/memfile_lease_storage.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/src/lib/dhcpsrv/mysql_connection.cc b/src/lib/dhcpsrv/mysql_connection.cc
index 30de103d53..06162bdf1e 100644
--- a/src/lib/dhcpsrv/mysql_connection.cc
+++ b/src/lib/dhcpsrv/mysql_connection.cc
@@ -12,7 +12,6 @@
#include <boost/lexical_cast.hpp>
#include <algorithm>
-#include <iterator>
#include <stdint.h>
#include <string>
#include <limits>
@@ -213,22 +212,26 @@ MySqlConnection::prepareStatement(uint32_t index, const char* text) {
}
void
-MySqlConnection::prepareStatements(const TaggedStatement tagged_statements[],
- size_t num_statements) {
- // Allocate space for all statements
- statements_.clear();
- statements_.resize(num_statements, NULL);
-
- text_statements_.clear();
- text_statements_.resize(num_statements, std::string(""));
-
+MySqlConnection::prepareStatements(const TaggedStatement* start_statement,
+ const TaggedStatement* end_statement) {
// Created the MySQL prepared statements for each DML statement.
- for (int i = 0; tagged_statements[i].text != NULL; ++i) {
- prepareStatement(tagged_statements[i].index,
- tagged_statements[i].text);
+ for (const TaggedStatement* tagged_statement = start_statement;
+ tagged_statement != end_statement; ++tagged_statement) {
+ if (tagged_statement->index >= statements_.size()) {
+ statements_.resize(tagged_statement->index + 1, NULL);
+ text_statements_.resize(tagged_statement->index + 1,
+ std::string(""));
+ }
+ prepareStatement(tagged_statement->index,
+ tagged_statement->text);
}
}
+void MySqlConnection::clearStatements() {
+ statements_.clear();
+ text_statements_.clear();
+}
+
/// @brief Destructor
MySqlConnection::~MySqlConnection() {
// Free up the prepared statements, ignoring errors. (What would we do
diff --git a/src/lib/dhcpsrv/mysql_connection.h b/src/lib/dhcpsrv/mysql_connection.h
index 8acb894577..4f84731bcd 100644
--- a/src/lib/dhcpsrv/mysql_connection.h
+++ b/src/lib/dhcpsrv/mysql_connection.h
@@ -240,15 +240,21 @@ public:
///
/// Creates the prepared statements for all of the SQL statements used
/// by the MySQL backend.
- /// @param tagged_statements an array of statements to be compiled
- /// @param num_statements number of statements in tagged_statements
+ ///
+ /// @param start_statement Pointer to the first statement in range of the
+ /// statements to be compiled.
+ /// @param end_statement Pointer to the statement marking end of the
+ /// range of statements to be compiled. This last statement is not compiled.
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
/// @throw isc::InvalidParameter 'index' is not valid for the vector. This
/// represents an internal error within the code.
- void prepareStatements(const TaggedStatement tagged_statements[],
- size_t num_statements);
+ void prepareStatements(const TaggedStatement* start_statement,
+ const TaggedStatement* end_statement);
+
+ /// @brief Clears prepared statements and text statements.
+ void clearStatements();
/// @brief Open Database
///
diff --git a/src/lib/dhcpsrv/mysql_host_data_source.cc b/src/lib/dhcpsrv/mysql_host_data_source.cc
index 7421facd5a..3dde4c4c04 100644
--- a/src/lib/dhcpsrv/mysql_host_data_source.cc
+++ b/src/lib/dhcpsrv/mysql_host_data_source.cc
@@ -11,6 +11,7 @@
#include <dhcp/option_definition.h>
#include <dhcp/option_space.h>
#include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/mysql_host_data_source.h>
#include <dhcpsrv/db_exceptions.h>
@@ -19,6 +20,7 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
+#include <boost/array.hpp>
#include <boost/pointer_cast.hpp>
#include <boost/static_assert.hpp>
@@ -960,7 +962,7 @@ private:
size_t start_column_;
/// @brief Option id.
- uint64_t option_id_;
+ uint32_t option_id_;
/// @brief Option code.
uint16_t code_;
@@ -1030,7 +1032,7 @@ private:
//@}
/// @brief Option id for last processed row.
- uint64_t most_recent_option_id_;
+ uint32_t most_recent_option_id_;
};
/// @brief Pointer to the @ref OptionProcessor class.
@@ -1227,7 +1229,7 @@ public:
/// @brief Returns last fetched reservation id.
///
/// @return Reservation id or 0 if no reservation data is fetched.
- uint64_t getReservationId() const {
+ uint32_t getReservationId() const {
if (reserv_type_null_ == MLM_FALSE) {
return (reservation_id_);
}
@@ -1365,7 +1367,7 @@ public:
private:
/// @brief IPv6 reservation id.
- uint64_t reservation_id_;
+ uint32_t reservation_id_;
/// @brief IPv6 reservation type.
uint8_t reserv_type_;
@@ -1386,7 +1388,7 @@ private:
uint8_t prefix_len_;
/// @brief IAID.
- uint8_t iaid_;
+ uint32_t iaid_;
/// @name Indexes of columns holding information about IPv6 reservations.
//@{
@@ -1408,7 +1410,7 @@ private:
//@}
/// @brief Reservation id for last processed row.
- uint64_t most_recent_reservation_id_;
+ uint32_t most_recent_reservation_id_;
};
@@ -1699,7 +1701,7 @@ private:
std::vector<uint8_t> value_;
/// @brief Option value length.
- size_t value_len_;
+ unsigned long value_len_;
/// @brief Formatted option value length.
unsigned long formatted_value_len_;
@@ -1708,7 +1710,7 @@ private:
std::string space_;
/// @brief Option space name length.
- size_t space_len_;
+ unsigned long space_len_;
/// @brief Boolean flag indicating if the option is always returned to
/// a client or only when requested.
@@ -1718,7 +1720,7 @@ private:
std::string client_class_;
/// @brief Length of the string holding client classes for the option.
- size_t client_class_len_;
+ unsigned long client_class_len_;
/// @brief Subnet identifier.
uint32_t subnet_id_;
@@ -1744,12 +1746,11 @@ public:
/// @brief Statement Tags
///
- /// The contents of the enum are indexes into the list of SQL statements
+ /// The contents of the enum are indexes into the list of SQL statements.
+ /// It is assumed that the order is such that the indicies of statements
+ /// reading the database are less than those of statements modifying the
+ /// database.
enum StatementIndex {
- INSERT_HOST, // Insert new host to collection
- INSERT_V6_RESRV, // Insert v6 reservation
- INSERT_V4_OPTION, // Insert DHCPv4 option
- INSERT_V6_OPTION, // Insert DHCPv6 option
GET_HOST_DHCPID, // Gets hosts by host identifier
GET_HOST_ADDR, // Gets hosts by IPv4 address
GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
@@ -1757,9 +1758,20 @@ public:
GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
GET_HOST_PREFIX, // Gets host by IPv6 prefix
GET_VERSION, // Obtain version number
+ INSERT_HOST, // Insert new host to collection
+ INSERT_V6_RESRV, // Insert v6 reservation
+ INSERT_V4_OPTION, // Insert DHCPv4 option
+ INSERT_V6_OPTION, // Insert DHCPv6 option
NUM_STATEMENTS // Number of statements
};
+ /// @brief Index of first statement performing write to the database.
+ ///
+ /// This value is used to mark border line between queries and other
+ /// statements and statements performing write operation on the database,
+ /// such as INSERT, DELETE, UPDATE.
+ static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST;
+
/// @brief Constructor.
///
/// This constructor opens database connection and initializes prepared
@@ -1865,6 +1877,15 @@ public:
StatementIndex stindex,
boost::shared_ptr<MySqlHostExchange> exchange) const;
+ /// @brief Throws exception if database is read only.
+ ///
+ /// This method should be called by the methods which write to the
+ /// database. If the backend is operating in read-only mode this
+ /// method will throw exception.
+ ///
+ /// @throw DbReadOnly if backend is operating in read only mode.
+ void checkReadOnly() const;
+
/// @brief Pointer to the object representing an exchange which
/// can be used to retrieve hosts and DHCPv4 options.
boost::shared_ptr<MySqlHostWithOptionsExchange> host_exchange_;
@@ -1890,40 +1911,18 @@ public:
/// @brief MySQL connection
MySqlConnection conn_;
+ /// @brief Indicates if the database is opened in read only mode.
+ bool is_readonly_;
};
-namespace {
-/// @brief Prepared MySQL statements used by the backend to insert and
-/// retrieve hosts from the database.
-TaggedStatement tagged_statements[] = {
- // Inserts a host into the 'hosts' table.
- {MySqlHostDataSourceImpl::INSERT_HOST,
- "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
- "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
- "dhcp4_client_classes, dhcp6_client_classes, dhcp4_next_server, "
- "dhcp4_server_hostname, dhcp4_boot_file_name) "
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
-
- // Inserts a single IPv6 reservation into 'reservations' table.
- {MySqlHostDataSourceImpl::INSERT_V6_RESRV,
- "INSERT INTO ipv6_reservations(address, prefix_len, type, "
- "dhcp6_iaid, host_id) "
- "VALUES (?,?,?,?,?)"},
- // Inserts a single DHCPv4 option into 'dhcp4_options' table.
- // Using fixed scope_id = 3, which associates an option with host.
- {MySqlHostDataSourceImpl::INSERT_V4_OPTION,
- "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
- "persistent, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id) "
- " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
-
- // Inserts a single DHCPv6 option into 'dhcp6_options' table.
- // Using fixed scope_id = 3, which associates an option with host.
- {MySqlHostDataSourceImpl::INSERT_V6_OPTION,
- "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
- "persistent, dhcp_client_class, dhcp6_subnet_id, host_id, scope_id) "
- " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
+/// @brief Array of tagged statements.
+typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
+TaggedStatementArray;
+/// @brief Prepared MySQL statements used by the backend to insert and
+/// retrieve hosts from the database.
+TaggedStatementArray tagged_statements = { {
// Retrieves host information, IPv6 reservations and both DHCPv4 and
// DHCPv6 options associated with the host. The LEFT JOIN clause is used
// to retrieve information from 4 different tables using a single query.
@@ -2051,11 +2050,34 @@ TaggedStatement tagged_statements[] = {
{MySqlHostDataSourceImpl::GET_VERSION,
"SELECT version, minor FROM schema_version"},
- // Marks the end of the statements table.
- {MySqlHostDataSourceImpl::NUM_STATEMENTS, NULL}
-};
+ // Inserts a host into the 'hosts' table.
+ {MySqlHostDataSourceImpl::INSERT_HOST,
+ "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
+ "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
+ "dhcp4_client_classes, dhcp6_client_classes, dhcp4_next_server, "
+ "dhcp4_server_hostname, dhcp4_boot_file_name) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
-}; // end anonymouse namespace
+ // Inserts a single IPv6 reservation into 'reservations' table.
+ {MySqlHostDataSourceImpl::INSERT_V6_RESRV,
+ "INSERT INTO ipv6_reservations(address, prefix_len, type, "
+ "dhcp6_iaid, host_id) "
+ "VALUES (?,?,?,?,?)"},
+
+ // Inserts a single DHCPv4 option into 'dhcp4_options' table.
+ // Using fixed scope_id = 3, which associates an option with host.
+ {MySqlHostDataSourceImpl::INSERT_V4_OPTION,
+ "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
+ "persistent, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id) "
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
+
+ // Inserts a single DHCPv6 option into 'dhcp6_options' table.
+ // Using fixed scope_id = 3, which associates an option with host.
+ {MySqlHostDataSourceImpl::INSERT_V6_OPTION,
+ "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
+ "persistent, dhcp_client_class, dhcp6_subnet_id, host_id, scope_id) "
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"}}
+};
MySqlHostDataSourceImpl::
MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
@@ -2065,23 +2087,43 @@ MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
DHCP4_AND_DHCP6)),
host_ipv6_reservation_exchange_(new MySqlIPv6ReservationExchange()),
host_option_exchange_(new MySqlOptionExchange()),
- conn_(parameters) {
+ conn_(parameters),
+ is_readonly_(false) {
// Open the database.
conn_.openDatabase();
- // Disable autocommit. To avoid a flush to disk on every commit, the global
- // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
- // cause the changes to be written to the log, but flushed to disk in the
- // background every second. Setting the parameter to that value will speed
- // up the system, but at the risk of losing data if the system crashes.
- my_bool result = mysql_autocommit(conn_.mysql_, 0);
+ // Enable autocommit. In case transaction is explicitly used, this
+ // setting will be overwritten for the transaction. However, there are
+ // cases when lack of autocommit could cause transactions to hang
+ // until commit or rollback is explicitly called. This already
+ // caused issues for some unit tests which were unable to cleanup
+ // the database after the test because of pending transactions.
+ // Use of autocommit will eliminate this problem.
+ my_bool result = mysql_autocommit(conn_.mysql_, 1);
if (result != 0) {
isc_throw(DbOperationError, mysql_error(conn_.mysql_));
}
- // Prepare all statements likely to be used.
- conn_.prepareStatements(tagged_statements, NUM_STATEMENTS);
+ // Prepare query statements. Those are will be only used to retrieve
+ // information from the database, so they can be used even if the
+ // database is read only for the current user.
+ conn_.prepareStatements(tagged_statements.begin(),
+ tagged_statements.begin() + WRITE_STMTS_BEGIN);
+
+ // Check if the backend is explicitly configured to operate with
+ // read only access to the database.
+ is_readonly_ = conn_.configuredReadOnly();
+
+ // If we are using read-write mode for the database we also prepare
+ // statements for INSERTS etc.
+ if (!is_readonly_) {
+ // Prepare statements for writing to the database, e.g. INSERT.
+ conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
+ tagged_statements.end());
+ } else {
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_MYSQL_HOST_DB_READONLY);
+ }
}
MySqlHostDataSourceImpl::~MySqlHostDataSourceImpl() {
@@ -2280,6 +2322,14 @@ getHost(const SubnetID& subnet_id,
return (result);
}
+void
+MySqlHostDataSourceImpl::checkReadOnly() const {
+ if (is_readonly_) {
+ isc_throw(ReadOnlyDb, "MySQL host database backend is configured to"
+ " operate in read only mode");
+ }
+}
+
MySqlHostDataSource::
MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
@@ -2292,6 +2342,9 @@ MySqlHostDataSource::~MySqlHostDataSource() {
void
MySqlHostDataSource::add(const HostPtr& host) {
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly();
+
// Initiate MySQL transaction as we will have to make multiple queries
// to insert host information into multiple tables. If that fails on
// any stage, the transaction will be rolled back by the destructor of
@@ -2614,12 +2667,16 @@ std::pair<uint32_t, uint32_t> MySqlHostDataSource::getVersion() const {
void
MySqlHostDataSource::commit() {
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly();
impl_->conn_.commit();
}
void
MySqlHostDataSource::rollback() {
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly();
impl_->conn_.rollback();
}
diff --git a/src/lib/dhcpsrv/mysql_host_data_source.h b/src/lib/dhcpsrv/mysql_host_data_source.h
index 9ce64f2256..d5e56949a0 100644
--- a/src/lib/dhcpsrv/mysql_host_data_source.h
+++ b/src/lib/dhcpsrv/mysql_host_data_source.h
@@ -8,6 +8,7 @@
#define MYSQL_HOST_DATA_SOURCE_H
#include <dhcpsrv/base_host_data_source.h>
+#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/mysql_connection.h>
namespace isc {
@@ -255,7 +256,7 @@ public:
private:
/// @brief Pointer to the implementation of the @ref MySqlHostDataSource.
- MySqlHostDataSourceImpl* impl_;
+ MySqlHostDataSourceImpl* impl_;
};
}
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc
index 410880af36..344fb272fc 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.cc
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -13,6 +13,7 @@
#include <dhcpsrv/mysql_lease_mgr.h>
#include <dhcpsrv/mysql_connection.h>
+#include <boost/array.hpp>
#include <boost/static_assert.hpp>
#include <mysqld_error.h>
@@ -70,6 +71,7 @@ using namespace std;
/// lease object.
namespace {
+
/// @brief Maximum length of the hostname stored in DNS.
///
/// This length is restricted by the length of the domain-name carried
@@ -82,7 +84,8 @@ const size_t HOSTNAME_MAX_LEN = 255;
/// colon separators.
const size_t ADDRESS6_TEXT_MAX_LEN = 39;
-TaggedStatement tagged_statements[] = {
+boost::array<TaggedStatement, MySqlLeaseMgr::NUM_STATEMENTS>
+tagged_statements = { {
{MySqlLeaseMgr::DELETE_LEASE4,
"DELETE FROM lease4 WHERE address = ?"},
{MySqlLeaseMgr::DELETE_LEASE4_STATE_EXPIRED,
@@ -205,8 +208,14 @@ TaggedStatement tagged_statements[] = {
"hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
"state = ? "
"WHERE address = ?"},
- // End of list sentinel
- {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
+ {MySqlLeaseMgr::RECOUNT_LEASE4_STATS,
+ "SELECT subnet_id, state, count(state) as state_count "
+ " FROM lease4 GROUP BY subnet_id, state ORDER BY subnet_id"},
+ {MySqlLeaseMgr::RECOUNT_LEASE6_STATS,
+ "SELECT subnet_id, lease_type, state, count(state) as state_count"
+ " FROM lease6 GROUP BY subnet_id, lease_type, state "
+ " ORDER BY subnet_id" }
+ }
};
};
@@ -1214,8 +1223,145 @@ private:
uint32_t state_; ///< Lease state.
};
+/// @brief MySql derivation of the statistical lease data query
+///
+/// This class is used to recalculate lease statistics for MySQL
+/// lease storage. It does so by executing a query which returns a result
+/// containining contain one row per monitored state per lease type per
+/// subnet, ordered by subnet id in ascending order.
+///
+class MySqlLeaseStatsQuery : public LeaseStatsQuery {
+public:
+ /// @brief Constructor
+ ///
+ /// @param conn A open connection to the database housing the lease data
+ /// @brief statement_index Index of the query's prepared statement
+ /// @brief fetch_type Indicates if query supplies lease type
+ MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
+ const bool fetch_type)
+ : conn_(conn), statement_index_(statement_index), statement_(NULL),
+ fetch_type_(fetch_type),
+ // Set the number of columns in the bind array based on fetch_type
+ // This is the number of columns expected in the result set
+ bind_(fetch_type_ ? 4 : 3) {
+ if (statement_index_ >= MySqlLeaseMgr::NUM_STATEMENTS) {
+ isc_throw(BadValue, "MySqlLeaseStatsQuery"
+ " - invalid statement index" << statement_index_);
+ }
+
+ statement_ = conn.statements_[statement_index_];
+ }
+
+ /// @brief Destructor
+ virtual ~MySqlLeaseStatsQuery() {
+ (void) mysql_stmt_free_result(statement_);
+ }
+ /// @brief Creates the IPv4 lease statistical data result set
+ ///
+ /// The result set is populated by executing a SQL query against the
+ /// lease(4/6) table which sums the leases per lease state per lease
+ /// type (v6 only) per subnet id. This method binds the statement to
+ /// the output bind array and then executes the statement, and fetches
+ /// entire result set.
+ void start() {
+ int col = 0;
+ // subnet_id: unsigned int
+ bind_[col].buffer_type = MYSQL_TYPE_LONG;
+ bind_[col].buffer = reinterpret_cast<char*>(&subnet_id_);
+ bind_[col].is_unsigned = MLM_TRUE;
+ ++col;
+
+ // Fetch the lease type if we were told to do so.
+ if (fetch_type_) {
+ // lease type: uint32_t
+ bind_[col].buffer_type = MYSQL_TYPE_LONG;
+ bind_[col].buffer = reinterpret_cast<char*>(&lease_type_);
+ bind_[col].is_unsigned = MLM_TRUE;
+ ++col;
+ } else {
+ fetch_type_ = Lease::TYPE_NA;
+ }
+ // state: uint32_t
+ bind_[col].buffer_type = MYSQL_TYPE_LONG;
+ bind_[col].buffer = reinterpret_cast<char*>(&lease_state_);
+ bind_[col].is_unsigned = MLM_TRUE;
+ ++col;
+
+ // state_count_: uint32_t
+ bind_[col].buffer_type = MYSQL_TYPE_LONG;
+ bind_[col].buffer = reinterpret_cast<char*>(&state_count_);
+ bind_[col].is_unsigned = MLM_TRUE;
+
+ // Set up the MYSQL_BIND array for the data being returned
+ // and bind it to the statement.
+ int status = mysql_stmt_bind_result(statement_, &bind_[0]);
+ conn_.checkError(status, statement_index_, "outbound binding failed");
+
+ // Execute the statement
+ status = mysql_stmt_execute(statement_);
+ conn_.checkError(status, statement_index_, "unable to execute");
+
+ // Ensure that all the lease information is retrieved in one go to avoid
+ // overhead of going back and forth between client and server.
+ status = mysql_stmt_store_result(statement_);
+ conn_.checkError(status, statement_index_, "results storage failed");
+ }
+
+
+ /// @brief Fetches the next row in the result set
+ ///
+ /// Once the internal result set has been populated by invoking the
+ /// the start() method, this method is used to iterate over the
+ /// result set rows. Once the last row has been fetched, subsequent
+ /// calls will return false.
+ ///
+ /// @param row Storage for the fetched row
+ ///
+ /// @return True if the fetch succeeded, false if there are no more
+ /// rows to fetch.
+ bool getNextRow(LeaseStatsRow& row) {
+ bool have_row = false;
+ int status = mysql_stmt_fetch(statement_);
+ if (status == MLM_MYSQL_FETCH_SUCCESS) {
+ row.subnet_id_ = static_cast<SubnetID>(subnet_id_);
+ row.lease_type_ = static_cast<Lease::Type>(lease_type_);
+ row.lease_state_ = lease_state_;
+ row.state_count_ = state_count_;
+ have_row = true;
+ } else if (status != MYSQL_NO_DATA) {
+ conn_.checkError(status, statement_index_, "getNextRow failed");
+ }
+
+ return (have_row);
+ }
+
+private:
+ /// @brief Database connection to use to execute the query
+ MySqlConnection& conn_;
+
+ /// @brief Index of the query's prepared statement
+ size_t statement_index_;
+
+ /// @brief The query's prepared statement
+ MYSQL_STMT *statement_;
+
+ /// @brief Indicates if query supplies lease type
+ bool fetch_type_;
+
+ /// @brief Bind array used to store the query result set;
+ std::vector<MYSQL_BIND> bind_;
+
+ /// @brief Receives subnet ID when fetching a row
+ uint32_t subnet_id_;
+ /// @brief Receives the lease type when fetching a row
+ uint32_t lease_type_;
+ /// @brief Receives the lease state when fetching a row
+ uint32_t lease_state_;
+ /// @brief Receives the state count when fetching a row
+ uint32_t state_count_;
+};
// MySqlLeaseMgr Constructor and Destructor
@@ -1236,7 +1382,7 @@ MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
}
// Prepare all statements likely to be used.
- conn_.prepareStatements(tagged_statements, MySqlLeaseMgr::NUM_STATEMENTS);
+ conn_.prepareStatements(tagged_statements.begin(), tagged_statements.end());
// Create the exchange objects for use in exchanging data between the
// program and the database.
@@ -2025,6 +2171,23 @@ MySqlLeaseMgr::getVersion() const {
return (std::make_pair(major, minor));
}
+LeaseStatsQueryPtr
+MySqlLeaseMgr::startLeaseStatsQuery4() {
+ LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
+ RECOUNT_LEASE4_STATS,
+ false));
+ query->start();
+ return(query);
+}
+
+LeaseStatsQueryPtr
+MySqlLeaseMgr::startLeaseStatsQuery6() {
+ LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
+ RECOUNT_LEASE6_STATS,
+ true));
+ query->start();
+ return(query);
+}
void
MySqlLeaseMgr::commit() {
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h
index a5824becbb..0e4b3af0b2 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.h
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.h
@@ -25,7 +25,6 @@ namespace dhcp {
class MySqlLease4Exchange;
class MySqlLease6Exchange;
-
/// @brief MySQL Lease Manager
///
/// This class provides the \ref isc::dhcp::LeaseMgr interface to the MySQL
@@ -410,6 +409,8 @@ public:
INSERT_LEASE6, // Add entry to lease6 table
UPDATE_LEASE4, // Update a Lease4 entry
UPDATE_LEASE6, // Update a Lease6 entry
+ RECOUNT_LEASE4_STATS, // Fetches IPv4 address statisics
+ RECOUNT_LEASE6_STATS, // Fetches IPv6 address statisics
NUM_STATEMENTS // Number of statements
};
@@ -590,6 +591,25 @@ private:
uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
StatementIndex statement_index);
+ /// @brief Creates and runs the IPv4 lease stats query
+ ///
+ /// It creates an instance of a MySqlLeaseStatsQuery4 and then
+ /// invokes its start method, which fetches its statistical data
+ /// result set by executing the RECOUNT_LEASE_STATS4 query.
+ /// The query object is then returned.
+ ///
+ /// @return The populated query as a pointer to an LeaseStatsQuery
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery4();
+
+ /// @brief Creates and runs the IPv6 lease stats query
+ ///
+ /// It creates an instance of a MySqlLeaseStatsQuery6 and then
+ /// invokes its start method, which fetches its statistical data
+ /// result set by executing the RECOUNT_LEASE_STATS6 query.
+ /// The query object is then returned.
+ ///
+ /// @return The populated query as a pointer to an LeaseStatsQuery
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery6();
/// @brief Check Error and Throw Exception
///
diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc
index 5386e9d2d6..2de5485175 100644
--- a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc
+++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc
@@ -53,7 +53,7 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
// 2. Update the copy with the passed keywords.
BOOST_FOREACH(ConfigPair param, config_value->mapValue()) {
try {
- if (param.first == "persist") {
+ if ((param.first == "persist") || (param.first == "readonly")) {
values_copy[param.first] = (param.second->boolValue() ?
"true" : "false");
@@ -72,7 +72,8 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
}
} catch (const isc::data::TypeError& ex) {
// Append position of the element.
- isc_throw(isc::data::TypeError, ex.what() << " ("
+ isc_throw(BadValue, "invalid value type specified for "
+ "parameter '" << param.first << "' ("
<< param.second->getPosition() << ")");
}
}
diff --git a/src/lib/dhcpsrv/pgsql_connection.cc b/src/lib/dhcpsrv/pgsql_connection.cc
index 9d722a458a..e79712c2fa 100644
--- a/src/lib/dhcpsrv/pgsql_connection.cc
+++ b/src/lib/dhcpsrv/pgsql_connection.cc
@@ -130,6 +130,16 @@ PgSqlConnection::prepareStatement(const PgSqlTaggedStatement& statement) {
}
void
+PgSqlConnection::prepareStatements(const PgSqlTaggedStatement* start_statement,
+ const PgSqlTaggedStatement* end_statement) {
+ // Created the PostgreSQL prepared statements.
+ for (const PgSqlTaggedStatement* tagged_statement = start_statement;
+ tagged_statement != end_statement; ++tagged_statement) {
+ prepareStatement(*tagged_statement);
+ }
+}
+
+void
PgSqlConnection::openDatabase() {
string dbconnparameters;
string shost = "localhost";
diff --git a/src/lib/dhcpsrv/pgsql_connection.h b/src/lib/dhcpsrv/pgsql_connection.h
index 92bcd4b5ed..b0d793b611 100644
--- a/src/lib/dhcpsrv/pgsql_connection.h
+++ b/src/lib/dhcpsrv/pgsql_connection.h
@@ -313,6 +313,21 @@ public:
/// failed.
void prepareStatement(const PgSqlTaggedStatement& statement);
+ /// @brief Prepare statements
+ ///
+ /// Creates the prepared statements for all of the SQL statements used
+ /// by the PostgreSQL backend.
+ ///
+ /// @param start_statement Pointer to the first statement in range of the
+ /// statements to be compiled.
+ /// @param end_statement Pointer to the statement marking end of the
+ /// range of statements to be compiled. This last statement is not compiled.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ void prepareStatements(const PgSqlTaggedStatement* start_statement,
+ const PgSqlTaggedStatement* end_statement);
+
/// @brief Open Database
///
/// Opens the database using the information supplied in the parameters
diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc
index ba886c9d0c..732db7cf1d 100644
--- a/src/lib/dhcpsrv/pgsql_host_data_source.cc
+++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc
@@ -10,6 +10,7 @@
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_space.h>
+#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/pgsql_host_data_source.h>
@@ -19,6 +20,7 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
+#include <boost/array.hpp>
#include <boost/pointer_cast.hpp>
#include <boost/static_assert.hpp>
@@ -35,7 +37,7 @@ namespace {
/// @brief Maximum length of option value.
/// The maximum size of the raw option data that may be read from the
-/// database.
+/// database.
const size_t OPTION_VALUE_MAX_LEN = 4096;
/// @brief Numeric value representing last supported identifier.
@@ -1134,12 +1136,11 @@ public:
/// @brief Statement Tags
///
- /// The contents of the enum are indexes into the list of SQL statements
+ /// The contents of the enum are indexes into the list of SQL statements.
+ /// It is assumed that the order is such that the indicies of statements
+ /// reading the database are less than those of statements modifying the
+ /// database.
enum StatementIndex {
- INSERT_HOST, // Insert new host to collection
- INSERT_V6_RESRV, // Insert v6 reservation
- INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
- INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
GET_HOST_DHCPID, // Gets hosts by host identifier
GET_HOST_ADDR, // Gets hosts by IPv4 address
GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
@@ -1147,9 +1148,20 @@ public:
GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
GET_HOST_PREFIX, // Gets host by IPv6 prefix
GET_VERSION, // Obtain version number
+ INSERT_HOST, // Insert new host to collection
+ INSERT_V6_RESRV, // Insert v6 reservation
+ INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
+ INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
NUM_STATEMENTS // Number of statements
};
+ /// @brief Index of first statement performing write to the database.
+ ///
+ /// This value is used to mark border line between queries and other
+ /// statements and statements performing write operation on the database,
+ /// such as INSERT, DELETE, UPDATE.
+ static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST;
+
/// @brief Constructor.
///
/// This constructor opens database connection and initializes prepared
@@ -1253,6 +1265,14 @@ public:
StatementIndex stindex,
boost::shared_ptr<PgSqlHostExchange> exchange) const;
+ /// @brief Throws exception if database is read only.
+ ///
+ /// This method should be called by the methods which write to the
+ /// database. If the backend is operating in read-only mode this
+ /// method will throw exception.
+ ///
+ /// @throw DbReadOnly if backend is operating in read only mode.
+ void checkReadOnly() const;
/// @brief Returns PostgreSQL schema version of the open database
///
@@ -1289,67 +1309,25 @@ public:
/// @brief MySQL connection
PgSqlConnection conn_;
+ /// @brief Indicates if the database is opened in read only mode.
+ bool is_readonly_;
};
namespace {
+/// @brief Array of tagged statements.
+typedef boost::array<PgSqlTaggedStatement, PgSqlHostDataSourceImpl::NUM_STATEMENTS>
+TaggedStatementArray;
+
/// @brief Prepared PosgreSQL statements used by the backend to insert and
/// retrieve reservation data from the database.
-PgSqlTaggedStatement tagged_statements[] = {
- // PgSqlHostDataSourceImpl::INSERT_HOST
- // Inserts a host into the 'hosts' table. Returns the inserted host id.
- {11,
- { OID_BYTEA, OID_INT2,
- OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR,
- OID_VARCHAR, OID_VARCHAR },
- "insert_host",
- "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
- " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
- " dhcp4_client_classes, dhcp6_client_classes, "
- " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name) "
- "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING host_id"
- },
-
- //PgSqlHostDataSourceImpl::INSERT_V6_RESRV
- // Inserts a single IPv6 reservation into 'reservations' table.
- {5,
- { OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 },
- "insert_v6_resrv",
- "INSERT INTO ipv6_reservations(address, prefix_len, type, "
- " dhcp6_iaid, host_id) "
- "VALUES ($1, $2, $3, $4, $5)"
- },
-
- // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
- // Inserts a single DHCPv4 option into 'dhcp4_options' table.
- // Using fixed scope_id = 3, which associates an option with host.
- {6,
- { OID_INT2, OID_BYTEA, OID_TEXT,
- OID_VARCHAR, OID_BOOL, OID_INT8},
- "insert_v4_host_option",
- "INSERT INTO dhcp4_options(code, value, formatted_value, space, "
- " persistent, host_id, scope_id) "
- "VALUES ($1, $2, $3, $4, $5, $6, 3)"
- },
-
- // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
- // Inserts a single DHCPv6 option into 'dhcp6_options' table.
- // Using fixed scope_id = 3, which associates an option with host.
- {6,
- { OID_INT2, OID_BYTEA, OID_TEXT,
- OID_VARCHAR, OID_BOOL, OID_INT8},
- "insert_v6_host_option",
- "INSERT INTO dhcp6_options(code, value, formatted_value, space, "
- " persistent, host_id, scope_id) "
- "VALUES ($1, $2, $3, $4, $5, $6, 3)"
- },
-
+TaggedStatementArray tagged_statements = { {
// PgSqlHostDataSourceImpl::GET_HOST_DHCPID
// Retrieves host information, IPv6 reservations and both DHCPv4 and
// DHCPv6 options associated with the host. The LEFT JOIN clause is used
// to retrieve information from 4 different tables using a single query.
// Hence, this query returns multiple rows for a single host.
- {2,
+ {2,
{ OID_BYTEA, OID_INT2 },
"get_host_dhcpid",
"SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
@@ -1454,7 +1432,7 @@ PgSqlTaggedStatement tagged_statements[] = {
// are returned due to left joining IPv6 reservations and DHCPv6 options.
// The number of rows returned is multiplication of number of existing
// IPv6 reservations and DHCPv6 options.
- {2,
+ {2,
{ OID_VARCHAR, OID_INT2 },
"get_host_prefix",
"SELECT h.host_id, h.dhcp_identifier, "
@@ -1477,14 +1455,60 @@ PgSqlTaggedStatement tagged_statements[] = {
// PgSqlHostDataSourceImpl::GET_VERSION
// Retrieves MySQL schema version.
- {0,
+ {0,
{ OID_NONE },
"get_version",
"SELECT version, minor FROM schema_version"
},
- // Marks the end of the statements table.
- {0, { 0 }, NULL, NULL}
+ // PgSqlHostDataSourceImpl::INSERT_HOST
+ // Inserts a host into the 'hosts' table. Returns the inserted host id.
+ {11,
+ { OID_BYTEA, OID_INT2,
+ OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR,
+ OID_VARCHAR, OID_VARCHAR },
+ "insert_host",
+ "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
+ " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
+ " dhcp4_client_classes, dhcp6_client_classes, "
+ " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name) "
+ "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING host_id"
+ },
+
+ //PgSqlHostDataSourceImpl::INSERT_V6_RESRV
+ // Inserts a single IPv6 reservation into 'reservations' table.
+ {5,
+ { OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 },
+ "insert_v6_resrv",
+ "INSERT INTO ipv6_reservations(address, prefix_len, type, "
+ " dhcp6_iaid, host_id) "
+ "VALUES ($1, $2, $3, $4, $5)"
+ },
+
+ // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
+ // Inserts a single DHCPv4 option into 'dhcp4_options' table.
+ // Using fixed scope_id = 3, which associates an option with host.
+ {6,
+ { OID_INT2, OID_BYTEA, OID_TEXT,
+ OID_VARCHAR, OID_BOOL, OID_INT8},
+ "insert_v4_host_option",
+ "INSERT INTO dhcp4_options(code, value, formatted_value, space, "
+ " persistent, host_id, scope_id) "
+ "VALUES ($1, $2, $3, $4, $5, $6, 3)"
+ },
+
+ // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
+ // Inserts a single DHCPv6 option into 'dhcp6_options' table.
+ // Using fixed scope_id = 3, which associates an option with host.
+ {6,
+ { OID_INT2, OID_BYTEA, OID_TEXT,
+ OID_VARCHAR, OID_BOOL, OID_INT8},
+ "insert_v6_host_option",
+ "INSERT INTO dhcp6_options(code, value, formatted_value, space, "
+ " persistent, host_id, scope_id) "
+ "VALUES ($1, $2, $3, $4, $5, $6, 3)"
+ }
+}
};
}; // end anonymous namespace
@@ -1497,20 +1521,27 @@ PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
DHCP4_AND_DHCP6)),
host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()),
host_option_exchange_(new PgSqlOptionExchange()),
- conn_(parameters) {
+ conn_(parameters),
+ is_readonly_(false) {
// Open the database.
conn_.openDatabase();
- int i = 0;
- for( ; tagged_statements[i].text != NULL ; ++i) {
- conn_.prepareStatement(tagged_statements[i]);
- }
+ conn_.prepareStatements(tagged_statements.begin(),
+ tagged_statements.begin() + WRITE_STMTS_BEGIN);
- // Just in case somebody foo-barred things
- if (i != NUM_STATEMENTS) {
- isc_throw(DbOpenError, "Number of statements prepared: " << i
- << " does not match expected count:" << NUM_STATEMENTS);
+ // Check if the backend is explicitly configured to operate with
+ // read only access to the database.
+ is_readonly_ = conn_.configuredReadOnly();
+
+ // If we are using read-write mode for the database we also prepare
+ // statements for INSERTS etc.
+ if (!is_readonly_) {
+ conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
+ tagged_statements.end());
+
+ } else {
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_HOST_DB_READONLY);
}
}
@@ -1670,6 +1701,15 @@ std::pair<uint32_t, uint32_t> PgSqlHostDataSourceImpl::getVersion() const {
return (std::make_pair<uint32_t, uint32_t>(version, minor));
}
+void
+PgSqlHostDataSourceImpl::checkReadOnly() const {
+ if (is_readonly_) {
+ isc_throw(ReadOnlyDb, "PostgreSQL host database backend is configured"
+ " to operate in read only mode");
+ }
+}
+
+
/*********** PgSqlHostDataSource *********************/
@@ -1684,6 +1724,9 @@ PgSqlHostDataSource::~PgSqlHostDataSource() {
void
PgSqlHostDataSource::add(const HostPtr& host) {
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly();
+
// Initiate PostgreSQL transaction as we will have to make multiple queries
// to insert host information into multiple tables. If that fails on
// any stage, the transaction will be rolled back by the destructor of
@@ -1932,5 +1975,20 @@ std::pair<uint32_t, uint32_t> PgSqlHostDataSource::getVersion() const {
return(impl_->getVersion());
}
+void
+PgSqlHostDataSource::commit() {
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly();
+ impl_->conn_.commit();
+}
+
+
+void
+PgSqlHostDataSource::rollback() {
+ // If operating in read-only mode, throw exception.
+ impl_->checkReadOnly();
+ impl_->conn_.rollback();
+}
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.h b/src/lib/dhcpsrv/pgsql_host_data_source.h
index 2d7ab6c10b..40ad840c86 100644
--- a/src/lib/dhcpsrv/pgsql_host_data_source.h
+++ b/src/lib/dhcpsrv/pgsql_host_data_source.h
@@ -273,6 +273,16 @@ public:
/// has failed.
virtual std::pair<uint32_t, uint32_t> getVersion() const;
+ /// @brief Commit Transactions
+ ///
+ /// Commits all pending database operations.
+ virtual void commit();
+
+ /// @brief Rollback Transactions
+ ///
+ /// Rolls back all pending database operations.
+ virtual void rollback();
+
private:
/// @brief Pointer to the implementation of the @ref PgSqlHostDataSource.
diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc
index f9a5ad28eb..3ea7475484 100644
--- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc
+++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc
@@ -26,7 +26,7 @@ using namespace std;
namespace {
-/// @todo TKM lease6 needs to accomodate hwaddr,hwtype, and hwaddr source
+/// @todo TKM lease6 needs to accomodate hwaddr,hwtype, and hwaddr source
/// columns. This is coverd by tickets #3557, #4530, and PR#9.
/// @brief Catalog of all the SQL statements currently supported. Note
@@ -201,6 +201,19 @@ PgSqlTaggedStatement tagged_statements[] = {
"state = $13 "
"WHERE address = $14"},
+ // RECOUNT_LEASE4_STATS,
+ { 0, { OID_NONE },
+ "recount_lease4_stats",
+ "SELECT subnet_id, state, count(state) as state_count "
+ "FROM lease4 GROUP BY subnet_id, state ORDER BY subnet_id"},
+
+ // RECOUNT_LEASE6_STATS,
+ { 0, { OID_NONE },
+ "recount_lease6_stats",
+ "SELECT subnet_id, lease_type, state, count(state) as state_count "
+ "FROM lease6 GROUP BY subnet_id, lease_type, state "
+ "ORDER BY subnet_id"},
+
// End of list sentinel
{ 0, { 0 }, NULL, NULL}
};
@@ -681,6 +694,107 @@ private:
//@}
};
+/// @brief Base PgSql derivation of the statistical lease data query
+///
+/// This class provides the functionality such as results storgae and row
+/// fetching common to fulfilling the statistical lease data query.
+///
+class PgSqlLeaseStatsQuery : public LeaseStatsQuery {
+public:
+ /// @brief Constructor
+ ///
+ /// @param conn A open connection to the database housing the lease data
+ /// @param statement The lease data SQL prepared statement to execute
+ /// @param fetch_statement Indicates whether or not lease_type should be
+ /// fetched from the result set
+ PgSqlLeaseStatsQuery(PgSqlConnection& conn, PgSqlTaggedStatement& statement,
+ const bool fetch_type)
+ : conn_(conn), statement_(statement), result_set_(), next_row_(0),
+ fetch_type_(fetch_type) {
+ }
+
+ /// @brief Destructor
+ virtual ~PgSqlLeaseStatsQuery() {};
+
+ /// @brief Creates the lease statistical data result set
+ ///
+ /// The result set is populated by executing a prepared SQL query
+ /// against the database which sums the leases per lease state per
+ /// subnet id.
+ void start() {
+ // The query has no parameters, so we only need it's name.
+ result_set_.reset(new PgSqlResult(PQexecPrepared(conn_, statement_.name,
+ 0, NULL, NULL, NULL, 0)));
+
+ conn_.checkStatementError(*result_set_, statement_);
+ }
+
+ /// @brief Fetches the next row in the result set
+ ///
+ /// Once the internal result set has been populated by invoking the
+ /// the start() method, this method is used to iterate over the
+ /// result set rows. Once the last row has been fetched, subsequent
+ /// calls will return false.
+ ///
+ /// @param row Storage for the fetched row
+ ///
+ /// @return True if the fetch succeeded, false if there are no more
+ /// rows to fetch.
+ bool getNextRow(LeaseStatsRow& row) {
+ // If we're past the end, punt.
+ if (next_row_ >= result_set_->getRows()) {
+ return (false);
+ }
+
+ // Fetch the subnet id.
+ uint32_t col = 0;
+ uint32_t subnet_id;
+ PgSqlExchange::getColumnValue(*result_set_, next_row_, col, subnet_id);
+ row.subnet_id_ = static_cast<SubnetID>(subnet_id);
+ ++col;
+
+ // Fetch the lease type if we were told to do so.
+ if (fetch_type_) {
+ uint32_t lease_type;
+ PgSqlExchange::getColumnValue(*result_set_, next_row_ , col,
+ lease_type);
+ row.lease_type_ = static_cast<Lease::Type>(lease_type);
+ ++col;
+ } else {
+ row.lease_type_ = Lease::TYPE_NA;
+ }
+
+ // Fetch the lease state.
+ PgSqlExchange::getColumnValue(*result_set_, next_row_ , col,
+ row.lease_state_);
+ ++col;
+
+ // Fetch the state count.
+ PgSqlExchange::getColumnValue(*result_set_, next_row_, col,
+ row.state_count_);
+
+ // Point to the next row.
+ ++next_row_;
+ return (true);
+ }
+
+protected:
+ /// @brief Database connection to use to execute the query
+ PgSqlConnection& conn_;
+
+ /// @brief The query's prepared statement
+ PgSqlTaggedStatement& statement_;
+
+ /// @brief The result set returned by Postgres.
+ boost::shared_ptr<PgSqlResult> result_set_;
+
+ /// @brief Index of the next row to fetch
+ uint32_t next_row_;
+
+ /// @brief Indicates if query supplies lease type
+ bool fetch_type_;
+};
+
PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
: LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
exchange6_(new PgSqlLease6Exchange()), conn_(parameters) {
@@ -1222,6 +1336,26 @@ PgSqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
return (deleteLeaseCommon(statement_index, bind_array));
}
+LeaseStatsQueryPtr
+PgSqlLeaseMgr::startLeaseStatsQuery4() {
+ LeaseStatsQueryPtr query(
+ new PgSqlLeaseStatsQuery(conn_,
+ tagged_statements[RECOUNT_LEASE4_STATS],
+ false));
+ query->start();
+ return(query);
+}
+
+LeaseStatsQueryPtr
+PgSqlLeaseMgr::startLeaseStatsQuery6() {
+ LeaseStatsQueryPtr query(
+ new PgSqlLeaseStatsQuery(conn_,
+ tagged_statements[RECOUNT_LEASE6_STATS],
+ true));
+ query->start();
+ return(query);
+}
+
string
PgSqlLeaseMgr::getName() const {
string name = "";
diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h
index d316f1b9f0..69924067a3 100644
--- a/src/lib/dhcpsrv/pgsql_lease_mgr.h
+++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h
@@ -317,6 +317,26 @@ public:
/// @return Number of leases deleted.
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs);
+ /// @brief Creates and runs the IPv4 lease stats query
+ ///
+ /// It creates an instance of a PgSqlLeaseStatsQuery4 and then
+ /// invokes its start method, which fetches its statistical data
+ /// result set by executing the RECOUNT_LEASE_STATS4 query.
+ /// The query object is then returned.
+ ///
+ /// @return The populated query as a pointer to an LeaseStatsQuery
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery4();
+
+ /// @brief Creates and runs the IPv6 lease stats query
+ ///
+ /// It creates an instance of a PgSqlLeaseStatsQuery and then
+ /// invokes its start method, which fetches its statistical data
+ /// result set by executing the RECOUNT_LEASE_STATS6 query.
+ /// The query object is then returned.
+ ///
+ /// @return The populated query as a pointer to an LeaseStatsQuery
+ virtual LeaseStatsQueryPtr startLeaseStatsQuery6();
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
@@ -385,6 +405,8 @@ public:
INSERT_LEASE6, // Add entry to lease6 table
UPDATE_LEASE4, // Update a Lease4 entry
UPDATE_LEASE6, // Update a Lease6 entry
+ RECOUNT_LEASE4_STATS, // Fetch IPv4 lease statistical data
+ RECOUNT_LEASE6_STATS, // Fetch IPv4 lease statistical data
NUM_STATEMENTS // Number of statements
};
diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc
index 84e29bd6fc..7c7830966f 100644
--- a/src/lib/dhcpsrv/srv_config.cc
+++ b/src/lib/dhcpsrv/srv_config.cc
@@ -7,6 +7,7 @@
#include <config.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/srv_config.h>
+#include <dhcpsrv/lease_mgr_factory.h>
#include <log/logger_manager.h>
#include <log/logger_specification.h>
#include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_*
@@ -164,11 +165,17 @@ SrvConfig::removeStatistics() {
void
SrvConfig::updateStatistics() {
-
- // Updates statistics for v4 and v6 subnets
- getCfgSubnets4()->updateStatistics();
-
- getCfgSubnets6()->updateStatistics();
+ // Updating subnet statistics involves updating lease statistics, which
+ // is done by the LeaseMgr. Since servers with subnets, must have a
+ // LeaseMgr, we do not bother updating subnet stats for servers without
+ // a lease manager, such as D2. @todo We should probably examine why
+ // "SrvConfig" is being used by D2.
+ if (LeaseMgrFactory::haveInstance()) {
+ // Updates statistics for v4 and v6 subnets
+ getCfgSubnets4()->updateStatistics();
+
+ getCfgSubnets6()->updateStatistics();
+ }
}
}
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc
index 8f86f6a5e9..764ad60ee1 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc
@@ -123,6 +123,9 @@ AllocEngine4Test::generateDeclinedLease(const std::string& addr,
AllocEngine6Test::AllocEngine6Test() {
CfgMgr::instance().clear();
+ // This lease mgr needs to exist to before configuration commits.
+ factory_.create("type=memfile universe=6 persist=false");
+
duid_ = DuidPtr(new DUID(std::vector<uint8_t>(8, 0x42)));
iaid_ = 42;
@@ -141,7 +144,6 @@ AllocEngine6Test::AllocEngine6Test() {
initFqdn("", false, false);
- factory_.create("type=memfile universe=6 persist=false");
}
void
@@ -525,6 +527,10 @@ AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start,
}
AllocEngine4Test::AllocEngine4Test() {
+
+ // This lease mgr needs to exist to before configuration commits.
+ factory_.create("type=memfile universe=4 persist=false");
+
// Create fresh instance of the HostMgr, and drop any previous HostMgr state.
HostMgr::instance().create();
@@ -548,7 +554,6 @@ AllocEngine4Test::AllocEngine4Test() {
initSubnet(IOAddress("192.0.2.100"), IOAddress("192.0.2.109"));
cfg_mgr.commit();
- factory_.create("type=memfile universe=4 persist=false");
// Create a default context. Note that remaining parameters must be
// assigned when needed.
diff --git a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc
index 03523d34b0..80e39370b1 100644
--- a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc
@@ -89,6 +89,7 @@ public:
/// @brief Destructor.
virtual ~CfgMySQLDbAccessTest() {
destroyMySQLSchema();
+ LeaseMgrFactory::destroy();
}
};
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index c9edb88768..a6de68d7dd 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -10,6 +10,7 @@
#include <dhcp/dhcp6.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <stats/stats_mgr.h>
@@ -280,6 +281,23 @@ public:
void clear() {
CfgMgr::instance().setVerbose(false);
CfgMgr::instance().clear();
+ LeaseMgrFactory::destroy();
+ }
+
+ /// @brief Creates instance of the backend.
+ ///
+ /// @param family AF_INET for v4, AF_INET6 for v6
+ void startBackend(int family = AF_INET) {
+ try {
+ std::ostringstream s;
+ s << "type=memfile persist=false " << (family == AF_INET6 ?
+ "universe=6" : "universe=4");
+ LeaseMgrFactory::create(s.str());
+ } catch (const std::exception& ex) {
+ std::cerr << "*** ERROR: unable to create instance of the Memfile\n"
+ " lease database backend: " << ex.what() << std::endl;
+ throw;
+ }
}
/// used in client classification (or just empty container for other tests)
@@ -575,6 +593,7 @@ TEST_F(CfgMgrTest, verbosity) {
TEST_F(CfgMgrTest, commitStats4) {
CfgMgr& cfg_mgr = CfgMgr::instance();
StatsMgr& stats_mgr = StatsMgr::instance();
+ startBackend(AF_INET);
// Let's prepare the "old" configuration: a subnet with id 123
// and pretend there were addresses assigned, so statistics are non-zero.
@@ -641,6 +660,7 @@ TEST_F(CfgMgrTest, clearStats4) {
TEST_F(CfgMgrTest, commitStats6) {
CfgMgr& cfg_mgr = CfgMgr::instance();
StatsMgr& stats_mgr = StatsMgr::instance();
+ startBackend(AF_INET6);
// Let's prepare the "old" configuration: a subnet with id 123
// and pretend there were addresses assigned, so statistics are non-zero.
diff --git a/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc
index 846808da1f..e71dc2530f 100644
--- a/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc
@@ -88,7 +88,7 @@ public:
}
// Add the keyword and value - make sure that they are quoted.
- // The parameters which are not quoted are persist and
+ // The parameters which are not quoted are persist, readonly and
// lfc-interval as they are boolean and integer respectively.
result += quote + keyval[i] + quote + colon + space;
if (!quoteValue(std::string(keyval[i]))) {
@@ -176,7 +176,8 @@ private:
/// @return true if the value of the parameter should be quoted.
bool quoteValue(const std::string& parameter) const {
return ((parameter != "persist") && (parameter != "lfc-interval") &&
- (parameter != "connect-timeout"));
+ (parameter != "connect-timeout") &&
+ (parameter != "readonly"));
}
};
@@ -560,4 +561,45 @@ TEST_F(DbAccessParserTest, getDbAccessString) {
EXPECT_EQ(dbaccess, "name=keatest type=mysql");
}
+// Check that the configuration is accepted for the valid value
+// of "readonly".
+TEST_F(DbAccessParserTest, validReadOnly) {
+ const char* config[] = {"type", "mysql",
+ "user", "keatest",
+ "password", "keatest",
+ "name", "keatest",
+ "readonly", "true",
+ NULL};
+
+ string json_config = toJson(config);
+ ConstElementPtr json_elements = Element::fromJSON(json_config);
+ EXPECT_TRUE(json_elements);
+
+ TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
+ EXPECT_NO_THROW(parser.build(json_elements));
+
+ checkAccessString("Valid readonly parameter",
+ parser.getDbAccessParameters(),
+ config);
+}
+
+// Check that for the invalid value of the "readonly" parameter
+// an exception is thrown.
+TEST_F(DbAccessParserTest, invalidReadOnly) {
+ const char* config[] = {"type", "mysql",
+ "user", "keatest",
+ "password", "keatest",
+ "name", "keatest",
+ "readonly", "1",
+ NULL};
+
+ string json_config = toJson(config);
+ ConstElementPtr json_elements = Element::fromJSON(json_config);
+ EXPECT_TRUE(json_elements);
+
+ TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
+ EXPECT_THROW(parser.build(json_elements), BadValue);
+}
+
+
}; // Anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc
index 009985e70c..515b6757b3 100644
--- a/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc
+++ b/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc
@@ -12,8 +12,10 @@
#include <dhcp/option_string.h>
#include <dhcp/option_int.h>
#include <dhcp/option_vendor.h>
+#include <dhcpsrv/host_data_source_factory.h>
#include <dhcpsrv/tests/generic_host_data_source_unittest.h>
#include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/testutils/schema.h>
#include <dhcpsrv/database_connection.h>
#include <asiolink/io_address.h>
#include <util/buffer.h>
@@ -507,6 +509,55 @@ GenericHostDataSourceTest::addTestOptions(const HostPtr& host,
LibDHCP::setRuntimeOptionDefs(defs);
}
+void
+GenericHostDataSourceTest::testReadOnlyDatabase(const char* valid_db_type) {
+ ASSERT_TRUE(hdsptr_);
+
+ // The database is initially opened in "read-write" mode. We can
+ // insert some data to the databse.
+ HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_DUID, false);
+ ASSERT_TRUE(host);
+ ASSERT_NO_THROW(hdsptr_->add(host));
+
+ // Subnet id will be used in queries to the database.
+ SubnetID subnet_id = host->getIPv6SubnetID();
+
+ // Make sure that the host has been inserted and that the data can be
+ // retrieved.
+ ConstHostPtr host_by_id = hdsptr_->get6(subnet_id, host->getIdentifierType(),
+ &host->getIdentifier()[0],
+ host->getIdentifier().size());
+ ASSERT_TRUE(host_by_id);
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
+
+ // Close the database connection and reopen in "read-only" mode as
+ // specified by the "VALID_READONLY_DB" parameter.
+ HostDataSourceFactory::destroy();
+ HostDataSourceFactory::create(connectionString(valid_db_type,
+ VALID_NAME,
+ VALID_HOST,
+ VALID_READONLY_USER,
+ VALID_PASSWORD,
+ VALID_READONLY_DB));
+
+ hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
+
+ // Check that an attempt to insert new host would result in
+ // exception.
+ HostPtr host2 = initializeHost6("2001:db8::2", Host::IDENT_DUID, false);
+ ASSERT_TRUE(host2);
+ ASSERT_THROW(hdsptr_->add(host2), ReadOnlyDb);
+ ASSERT_THROW(hdsptr_->commit(), ReadOnlyDb);
+ ASSERT_THROW(hdsptr_->rollback(), ReadOnlyDb);
+
+ // Reading from the database should still be possible, though.
+ host_by_id = hdsptr_->get6(subnet_id, host->getIdentifierType(),
+ &host->getIdentifier()[0],
+ host->getIdentifier().size());
+ ASSERT_TRUE(host_by_id);
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
+}
+
void GenericHostDataSourceTest::testBasic4(const Host::IdentifierType& id) {
// Make sure we have the pointer to the host data source.
ASSERT_TRUE(hdsptr_);
diff --git a/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h b/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h
index 79b9f97b33..807ca21490 100644
--- a/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h
+++ b/src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h
@@ -337,6 +337,23 @@ public:
/// @brief Pointer to the host data source
HostDataSourcePtr hdsptr_;
+ /// @brief Test that backend can be started in read-only mode.
+ ///
+ /// Some backends can operate when the database is read only, e.g.
+ /// host reservation tables are read only, or the database user has
+ /// read only privileges on the entire database. In such cases, the
+ /// Kea server administrator can specify in the backend configuration
+ /// that the database should be opened in read only mode, i.e.
+ /// INSERT, UPDATE, DELETE statements can't be issued. If any of the
+ /// functions updating the database is called for the backend, the
+ /// error is reported. The database running in read only mode can
+ /// be merely used to retrieve existing host reservations from the
+ /// database. This test verifies that this is the case.
+ ///
+ /// @param valid_db_type Parameter specifying type of backend to
+ /// be used, e.g. type=mysql.
+ void testReadOnlyDatabase(const char* valid_db_type);
+
/// @brief Test that checks that simple host with IPv4 reservation
/// can be inserted and later retrieved.
///
diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
index a94a3a2b5c..25d1b2bd07 100644
--- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
@@ -5,11 +5,18 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/database_connection.h>
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
#include <dhcpsrv/tests/test_utils.h>
-#include <dhcpsrv/database_connection.h>
-#include <asiolink/io_address.h>
+#include <stats/stats_mgr.h>
+
+#include <boost/foreach.hpp>
+
#include <gtest/gtest.h>
+
#include <sstream>
using namespace std;
@@ -57,6 +64,7 @@ GenericLeaseMgrTest::GenericLeaseMgrTest()
/// a template
leasetype6_.push_back(LEASETYPE6[i]);
}
+
}
GenericLeaseMgrTest::~GenericLeaseMgrTest() {
@@ -2381,6 +2389,300 @@ GenericLeaseMgrTest::testGetDeclinedLeases6() {
}
}
+void
+GenericLeaseMgrTest::checkStat(const std::string& name,
+ const int64_t expected_value) {
+ stats::ObservationPtr obs =
+ stats::StatsMgr::instance().getObservation(name);
+
+ ASSERT_TRUE(obs) << " stat: " << name << " not found ";
+ ASSERT_EQ(expected_value, obs->getInteger().first)
+ << " stat: " << name << " value wrong";
+}
+
+void
+GenericLeaseMgrTest::checkLeaseStats(const StatValMapList& expectedStats) {
+ // Global accumulators
+ int64_t declined_addresses = 0;
+ int64_t declined_reclaimed_addresses = 0;
+
+ // Iterate over all stats for each subnet
+ for (int subnet_idx = 0; subnet_idx < expectedStats.size(); ++subnet_idx) {
+ BOOST_FOREACH(StatValPair expectedStat, expectedStats[subnet_idx]) {
+ // Verify the per subnet value.
+ checkStat(stats::StatsMgr::generateName("subnet", subnet_idx+1,
+ expectedStat.first),
+ expectedStat.second);
+
+ // Add the value to globals as needed.
+ if (expectedStat.first == "declined-addresses") {
+ declined_addresses += expectedStat.second;
+ } else if (expectedStat.first == "declined-reclaimed-addresses") {
+ declined_reclaimed_addresses += expectedStat.second;
+ }
+ }
+ }
+
+ // Verify the globals.
+ checkStat("declined-addresses", declined_addresses);
+ checkStat("declined-reclaimed-addresses", declined_reclaimed_addresses);
+}
+
+void
+GenericLeaseMgrTest::makeLease4(const std::string& address,
+ const SubnetID& subnet_id,
+ const uint32_t state) {
+ Lease4Ptr lease(new Lease4());
+
+ // set the address
+ lease->addr_ = IOAddress(address);
+
+ // make a MAC from the address
+ std::vector<uint8_t> hwaddr = lease->addr_.toBytes();
+ hwaddr.push_back(0);
+ hwaddr.push_back(0);
+
+ lease->hwaddr_.reset(new HWAddr(hwaddr, HTYPE_ETHER));
+ lease->valid_lft_ = 86400;
+ lease->cltt_ = 168256;
+ lease->subnet_id_ = subnet_id;
+ lease->state_ = state;
+ ASSERT_TRUE(lmptr_->addLease(lease));
+}
+
+void
+GenericLeaseMgrTest::makeLease6(const Lease::Type& type,
+ const std::string& address,
+ uint8_t prefix_len,
+ const SubnetID& subnet_id,
+ const uint32_t state) {
+ IOAddress addr(address);
+
+ // make a DUID from the address
+ std::vector<uint8_t> bytes = addr.toBytes();
+ bytes.push_back(prefix_len);
+
+ Lease6Ptr lease(new Lease6(type, addr, DuidPtr(new DUID(bytes)), 77,
+ 16000, 24000, 0, 0, subnet_id, HWAddrPtr(),
+ prefix_len));
+ lease->state_ = state;
+ ASSERT_TRUE(lmptr_->addLease(lease));
+}
+
+void
+GenericLeaseMgrTest::testRecountLeaseStats4() {
+ using namespace stats;
+
+ StatsMgr::instance().removeAll();
+
+ // Create two subnets.
+ int num_subnets = 2;
+ CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4();
+ Subnet4Ptr subnet;
+ Pool4Ptr pool;
+
+ subnet.reset(new Subnet4(IOAddress("192.0.1.0"), 24, 1, 2, 3, 1));
+ pool.reset(new Pool4(IOAddress("192.0.1.0"), 24));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ subnet.reset(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 2));
+ pool.reset(new Pool4(IOAddress("192.0.2.0"), 24));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+ // Create the expected stats list. At this point, the only stat
+ // that should be non-zero is total-addresses.
+ StatValMapList expectedStats(num_subnets);
+ for (int i = 0; i < num_subnets; ++i) {
+ expectedStats[i]["total-addresses"] = 256;
+ expectedStats[i]["assigned-addresses"] = 0;
+ expectedStats[i]["declined-addresses"] = 0;
+ expectedStats[i]["declined-reclaimed-addresses"] = 0;
+ }
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Recount stats. We should have the same results.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Now let's insert some leases into subnet 1.
+ int subnet_id = 1;
+
+ // Insert one lease in default state, i.e. assigned.
+ makeLease4("192.0.1.1", subnet_id);
+
+ // Insert one lease in declined state.
+ makeLease4("192.0.1.2", subnet_id, Lease::STATE_DECLINED);
+
+ // Insert one lease in the expired state.
+ makeLease4("192.0.1.3", subnet_id, Lease::STATE_EXPIRED_RECLAIMED);
+
+ // Insert another lease in default state, i.e. assigned.
+ makeLease4("192.0.1.4", subnet_id);
+
+ // Update the expected stats list for subnet 1.
+ expectedStats[subnet_id - 1]["assigned-addresses"] = 2;
+ expectedStats[subnet_id - 1]["declined-addresses"] = 1;
+
+ // Now let's add leases to subnet 2.
+ subnet_id = 2;
+
+ // Insert one delined lease.
+ makeLease4("192.0.2.2", subnet_id, Lease::STATE_DECLINED);
+
+ // Update the expected stats.
+ expectedStats[subnet_id - 1]["declined-addresses"] = 1;
+
+ // Now Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Delete some leases from subnet, and update the expected stats.
+ EXPECT_TRUE(lmptr_->deleteLease(IOAddress("192.0.1.1")));
+ expectedStats[0]["assigned-addresses"] = 1;
+
+ EXPECT_TRUE(lmptr_->deleteLease(IOAddress("192.0.1.2")));
+ expectedStats[0]["declined-addresses"] = 0;
+
+ // Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+}
+
+
+void
+GenericLeaseMgrTest::testRecountLeaseStats6() {
+ using namespace stats;
+
+ StatsMgr::instance().removeAll();
+
+ // Create two subnets.
+ int num_subnets = 2;
+ CfgSubnets6Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets6();
+ Subnet6Ptr subnet;
+ Pool6Ptr pool;
+ StatValMapList expectedStats(num_subnets);
+
+ int subnet_id = 1;
+ subnet.reset(new Subnet6(IOAddress("3001:1::"), 64, 1, 2, 3, 4, subnet_id));
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("3001:1::"),
+ IOAddress("3001:1::FF")));
+ subnet->addPool(pool);
+ expectedStats[subnet_id - 1]["total-nas"] = 256;
+
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("3001:1:2::"),96,112));
+ subnet->addPool(pool);
+ expectedStats[subnet_id - 1]["total-pds"] = 65536;
+ cfg->add(subnet);
+
+ ++subnet_id;
+ subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4,
+ subnet_id));
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 120));
+ subnet->addPool(pool);
+ expectedStats[subnet_id - 1]["total-nas"] = 256;
+ expectedStats[subnet_id - 1]["total-pds"] = 0;
+ cfg->add(subnet);
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+
+ // Create the expected stats list. At this point, the only stat
+ // that should be non-zero is total-nas/total-pds.
+ for (int i = 0; i < num_subnets; ++i) {
+ expectedStats[i]["assigned-nas"] = 0;
+ expectedStats[i]["declined-addresses"] = 0;
+ expectedStats[i]["declined-reclaimed-addresses"] = 0;
+ expectedStats[i]["assigned-pds"] = 0;
+ }
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+
+ // Recount stats. We should have the same results.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Now let's insert some leases into subnet 1.
+ subnet_id = 1;
+
+ // Insert three assigned NAs.
+ makeLease6(Lease::TYPE_NA, "3001:1::1", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "3001:1::2", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "3001:1::3", 0, subnet_id);
+ expectedStats[subnet_id - 1]["assigned-nas"] = 3;
+
+ // Insert two declined NAs.
+ makeLease6(Lease::TYPE_NA, "3001:1::4", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ makeLease6(Lease::TYPE_NA, "3001:1::5", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ expectedStats[subnet_id - 1]["declined-addresses"] = 2;
+
+ // Insert one expired NA.
+ makeLease6(Lease::TYPE_NA, "3001:1::6", 0, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+
+ // Insert two assigned PDs.
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0100::", 112, subnet_id);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0200::", 112, subnet_id);
+ expectedStats[subnet_id - 1]["assigned-pds"] = 2;
+
+ // Insert two expired PDs.
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0300::", 112, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0400::", 112, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+
+ // Now let's add leases to subnet 2.
+ subnet_id = 2;
+
+ // Insert two assigned NAs.
+ makeLease6(Lease::TYPE_NA, "2001:db81::1", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "2001:db81::2", 0, subnet_id);
+ expectedStats[subnet_id - 1]["assigned-nas"] = 2;
+
+ // Insert one declined NA.
+ makeLease6(Lease::TYPE_NA, "2001:db81::3", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ expectedStats[subnet_id - 1]["declined-addresses"] = 1;
+
+ // Now Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats6());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Delete some leases and update the expected stats.
+ EXPECT_TRUE(lmptr_->deleteLease(IOAddress("3001:1::2")));
+ expectedStats[0]["assigned-nas"] = 2;
+
+ EXPECT_TRUE(lmptr_->deleteLease(IOAddress("2001:db81::3")));
+ expectedStats[1]["declined-addresses"] = 0;
+
+ // Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats6());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+}
+
+
}; // namespace test
}; // namespace dhcp
}; // namespace isc
diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h
index 3ef1405503..f426787833 100644
--- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h
+++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -15,6 +15,12 @@ namespace isc {
namespace dhcp {
namespace test {
+
+/// @brief typedefs to simplify lease statistic testing
+typedef std::map<std::string, int64_t> StatValMap;
+typedef std::pair<std::string, int64_t> StatValPair;
+typedef std::vector<StatValMap> StatValMapList;
+
/// @brief Test Fixture class with utility functions for LeaseMgr backends
///
/// It contains utility functions, like dummy lease creation.
@@ -94,6 +100,47 @@ public:
/// @return vector<Lease6Ptr> Vector of pointers to leases
std::vector<Lease6Ptr> createLeases6();
+ /// @brief Compares a StatsMgr statistic to an expected value
+ ///
+ /// Attempt to fetch the named statistic from the StatsMg and if
+ /// found, compare its observed value to the given value.
+ /// Fails if the stat is not found or if the values do not match.
+ ///
+ /// @param name StatsMgr name for the statistic to check
+ /// @param expected_value expected value of the statistic
+ void checkStat(const std::string& name, const int64_t expected_value);
+
+ /// @brief Compares StatsMgr statistics against an expected list of values
+ ///
+ /// Iterates over a list of statistic names and expected values, attempting
+ /// to fetch each from the StatsMgr and if found, compare its observed value
+ /// to the expected value. Fails if any of the expected stats are not
+ /// found or if the values do not match.
+ ///
+ /// @param expected_stats Map of expected static names and values.
+ void checkLeaseStats(const StatValMapList& expected_stats);
+
+ /// @brief Constructs a minimal IPv4 lease and adds it to the lease storage
+ ///
+ /// @param address - IPv4 address for the lease
+ /// @param subnet_id - subnet ID to which the lease belongs
+ /// @param state - the state of the lease
+ void makeLease4(const std::string& address, const SubnetID& subnet_id,
+ const uint32_t state = Lease::STATE_DEFAULT);
+
+ /// @brief Constructs a minimal IPv6 lease and adds it to the lease storage
+ ///
+ /// The DUID is constructed from the address and prefix length.
+ ///
+ /// @param type - type of lease to create (TYPE_NA, TYPE_PD...)
+ /// @param address - IPv6 address/prefix for the lease
+ /// @param prefix_len = length of the prefix (should be 0 for TYPE_NA)
+ /// @param subnet_id - subnet ID to which the lease belongs
+ /// @param state - the state of the lease
+ void makeLease6(const Lease::Type& type, const std::string& address,
+ uint8_t prefix_len, const SubnetID& subnet_id,
+ const uint32_t state = Lease::STATE_DEFAULT);
+
/// @brief checks that addLease, getLease4(addr) and deleteLease() works
void testBasicLease4();
@@ -313,6 +360,20 @@ public:
/// leases can be removed.
void testDeleteExpiredReclaimedLeases4();
+ /// @brief Check that the IPv4 lease statistics can be recounted
+ ///
+ /// This test creates two subnets and several leases associated with
+ /// them, then verifies that lease statistics are recalculated correctly
+ /// after altering the lease states in various ways.
+ void testRecountLeaseStats4();
+
+ /// @brief Check that the IPv6 lease statistics can be recounted
+ ///
+ /// This test creates two subnets and several leases associated with
+ /// them, then verifies that lease statistics are recalculated correctly
+ /// after altering the lease states in various ways.
+ void testRecountLeaseStats6();
+
/// @brief String forms of IPv4 addresses
std::vector<std::string> straddress4_;
diff --git a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc
index 2abad6c444..8e922d355d 100644
--- a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc
@@ -9,7 +9,17 @@
#include <dhcp/hwaddr.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/host.h>
+#include <dhcpsrv/host_data_source_factory.h>
#include <dhcpsrv/host_mgr.h>
+
+#if defined HAVE_MYSQL
+#include <dhcpsrv/testutils/mysql_schema.h>
+#endif
+
+#if defined HAVE_PGSQL
+#include <dhcpsrv/testutils/pgsql_schema.h>
+#endif
+
#include <gtest/gtest.h>
#include <vector>
@@ -37,6 +47,85 @@ protected:
/// in the @c CfgMgr.
CfgHostsPtr getCfgHosts() const;
+ /// @brief Inserts IPv4 reservation into the host data source.
+ ///
+ /// @param data_source Reference to the data source to which the reservation
+ /// should be inserted.
+ /// @param hwaddr Pointer to the hardware address to be associated with the
+ /// reservation.
+ /// @param subnet_id IPv4 subnet id.
+ /// @param address IPv4 address to be reserved.
+ void addHost4(BaseHostDataSource& data_source,
+ const HWAddrPtr& hwaddr,
+ const SubnetID& subnet_id,
+ const IOAddress& address);
+
+ /// @brief Inserts IPv6 reservation into the host data source.
+ ///
+ /// @param data_source Reference to the data source to which the reservation
+ /// should be inserted.
+ /// @param duid Pointer to the DUID to be associated with the reservation.
+ /// @param subnet_id IPv6 subnet id.
+ /// @param address IPv6 address/prefix to be reserved.
+ /// @param prefix_len Prefix length. The default value is 128 which
+ /// indicates that the reservation is for an IPv6 address rather than a
+ /// prefix.
+ void addHost6(BaseHostDataSource& data_source,
+ const DuidPtr& duid,
+ const SubnetID& subnet_id,
+ const IOAddress& address,
+ const uint8_t prefix_len = 128);
+
+ /// @brief This test verifies that HostMgr returns all reservations for the
+ /// specified HW address.
+ ///
+ /// If reservations are added to different host data sources, it is expected
+ /// that the @c HostMgr will retrieve reservations from both of them.
+ ///
+ /// @param data_source1 Host data source to which first reservation is
+ /// inserted.
+ /// @param data_source2 Host data source to which second reservation is
+ /// inserted.
+ void testGetAll(BaseHostDataSource& data_source1,
+ BaseHostDataSource& data_source2);
+
+ /// @brief This test verifies that it is possible to retrieve IPv4
+ /// reservation for the particular host using HostMgr.
+ ///
+ /// If reservations are added to different host data sources, it is expected
+ /// that the @c HostMgr will retrieve reservations from both of them.
+ ///
+ /// @param data_source1 Host data source to which first reservation is
+ /// inserted.
+ /// @param data_source2 Host data source to which second reservation is
+ /// inserted.
+ void testGetAll4(BaseHostDataSource& data_source1,
+ BaseHostDataSource& data_source2);
+
+ /// @brief This test verifies that it is possible to retrieve an IPv4
+ /// reservation for the particular host using HostMgr.
+ ///
+ /// @param data_source Host data source to which reservation is inserted and
+ /// from which it will be retrieved.
+ void testGet4(BaseHostDataSource& data_source);
+
+ /// @brief This test verifies that it is possible to retrieve an IPv6
+ /// reservation for the particular host using HostMgr.
+ ///
+ /// @param data_source Host data source to which reservation is inserted and
+ /// from which it will be retrieved.
+ void testGet6(BaseHostDataSource& data_source);
+
+ /// @brief This test verifies that it is possible to retrieve an IPv6
+ /// prefix reservation for the particular host using HostMgr.
+ ///
+ /// @param data_source1 Host data source to which first reservation is
+ /// inserted.
+ /// @param data_source2 Host data source to which second reservation is
+ /// inserted.
+ void testGet6ByPrefix(BaseHostDataSource& data_source1,
+ BaseHostDataSource& data_source2);
+
/// @brief HW addresses to be used by the tests.
std::vector<HWAddrPtr> hwaddrs_;
/// @brief DUIDs to be used by the tests.
@@ -79,29 +168,52 @@ HostMgrTest::getCfgHosts() const {
return (CfgMgr::instance().getStagingCfg()->getCfgHosts());
}
-/// This test verifies that HostMgr returns all reservations for the
-/// specified HW address. The reservations are defined in the server's
-/// configuration.
-TEST_F(HostMgrTest, getAll) {
+void
+HostMgrTest::addHost4(BaseHostDataSource& data_source,
+ const HWAddrPtr& hwaddr,
+ const SubnetID& subnet_id,
+ const IOAddress& address) {
+ data_source.add(HostPtr(new Host(hwaddr->toText(false),
+ "hw-address", subnet_id, SubnetID(0),
+ address)));
+}
+
+void
+HostMgrTest::addHost6(BaseHostDataSource& data_source,
+ const DuidPtr& duid,
+ const SubnetID& subnet_id,
+ const IOAddress& address,
+ const uint8_t prefix_len) {
+ HostPtr new_host(new Host(duid->toText(), "duid", SubnetID(1),
+ subnet_id, IOAddress::IPV4_ZERO_ADDRESS()));
+ new_host->addReservation(IPv6Resrv(prefix_len == 128 ? IPv6Resrv::TYPE_NA :
+ IPv6Resrv::TYPE_PD,
+ address, prefix_len));
+ data_source.add(new_host);
+}
+
+
+void
+HostMgrTest::testGetAll(BaseHostDataSource& data_source1,
+ BaseHostDataSource& data_source2) {
// Initially, no reservations should be present.
ConstHostCollection hosts = HostMgr::instance().getAll(hwaddrs_[0]);
ASSERT_TRUE(hosts.empty());
// Add two reservations for the same HW address. They differ by the IP
// address reserved and the IPv4 subnet.
- getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
- "hw-address", SubnetID(1), SubnetID(0),
- IOAddress("192.0.2.5"))));
- getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
- "hw-address", SubnetID(10), SubnetID(0),
- IOAddress("192.0.3.10"))));
+ addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5"));
+ addHost4(data_source2, hwaddrs_[0], SubnetID(10), IOAddress("192.0.3.10"));
+
CfgMgr::instance().commit();
// If there non-matching HW address is specified, nothing should be
// returned.
- ASSERT_TRUE(HostMgr::instance().getAll(Host::IDENT_HWADDR,
+ hosts = HostMgr::instance().getAll(Host::IDENT_HWADDR,
&hwaddrs_[1]->hwaddr_[0],
- hwaddrs_[1]->hwaddr_.size()).empty());
+ hwaddrs_[1]->hwaddr_.size());
+ ASSERT_TRUE(hosts.empty());
+
// For the correct HW address, there should be two reservations.
hosts = HostMgr::instance().getAll(Host::IDENT_HWADDR,
&hwaddrs_[0]->hwaddr_[0],
@@ -136,26 +248,24 @@ TEST_F(HostMgrTest, getAll) {
ADD_FAILURE() << "Reservation for the IPv4 address 192.0.3.10"
" not found using getAll method";
}
-
}
-// This test verifies that it is possible to gather all reservations for the
-// specified IPv4 address from the HostMgr. The reservations are specified in
-// the server's configuration.
-TEST_F(HostMgrTest, getAll4) {
+void
+HostMgrTest::testGetAll4(BaseHostDataSource& data_source1,
+ BaseHostDataSource& data_source2) {
+ // Initially, no hosts should be present.
ConstHostCollection hosts =
HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
ASSERT_TRUE(hosts.empty());
- getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
- "hw-address", SubnetID(1), SubnetID(0),
- IOAddress("192.0.2.5"))));
+ // Add two hosts to different data sources.
+ addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5"));
+ addHost4(data_source2, hwaddrs_[1], SubnetID(10), IOAddress("192.0.2.5"));
- getCfgHosts()->add(HostPtr(new Host(hwaddrs_[1]->toText(false),
- "hw-address", SubnetID(10), SubnetID(0),
- IOAddress("192.0.2.5"))));
CfgMgr::instance().commit();
+ // Retrieve all hosts, This should return hosts from both sources
+ // in a single container.
hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
ASSERT_EQ(2, hosts.size());
@@ -167,19 +277,18 @@ TEST_F(HostMgrTest, getAll4) {
EXPECT_NE(hosts[0]->getIPv4SubnetID(), hosts[1]->getIPv4SubnetID());
}
-// This test verifies that it is possible to retrieve a reservation for the
-// particular host using HostMgr. The reservation is specified in the server's
-// configuration.
-TEST_F(HostMgrTest, get4) {
+void
+HostMgrTest::testGet4(BaseHostDataSource& data_source) {
+ // Initially, no host should be present.
ConstHostPtr host = HostMgr::instance().get4(SubnetID(1), hwaddrs_[0]);
ASSERT_FALSE(host);
- getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
- "hw-address",
- SubnetID(1), SubnetID(2),
- IOAddress("192.0.2.5"))));
+ // Add new host to the database.
+ addHost4(data_source, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5"));
+
CfgMgr::instance().commit();
+ // Retrieve the host from the database and expect that the parameters match.
host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_HWADDR,
&hwaddrs_[0]->hwaddr_[0],
hwaddrs_[0]->hwaddr_.size());
@@ -188,20 +297,18 @@ TEST_F(HostMgrTest, get4) {
EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText());
}
-// This test verifies that it is possible to retrieve IPv6 reservations for
-// the particular host using HostMgr. The reservation is specified in the
-// server's configuration.
-TEST_F(HostMgrTest, get6) {
+void
+HostMgrTest::testGet6(BaseHostDataSource& data_source) {
+ // Initially, no host should be present.
ConstHostPtr host = HostMgr::instance().get6(SubnetID(2), duids_[0]);
ASSERT_FALSE(host);
- HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1),
- SubnetID(2), IOAddress("0.0.0.0")));
- new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
- IOAddress("2001:db8:1::1")));
- getCfgHosts()->add(new_host);
+ // Add new host to the database.
+ addHost6(data_source, duids_[0], SubnetID(2), IOAddress("2001:db8:1::1"));
+
CfgMgr::instance().commit();
+ // Retrieve the host from the database and expect that the parameters match.
host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_DUID,
&duids_[0]->getDuid()[0],
duids_[0]->getDuid().size());
@@ -210,32 +317,25 @@ TEST_F(HostMgrTest, get6) {
IOAddress("2001:db8:1::1"))));
}
-// This test verifies that it is possible to retrieve the reservation of the
-// particular IPv6 prefix using HostMgr.
-TEST_F(HostMgrTest, get6ByPrefix) {
+void
+HostMgrTest::testGet6ByPrefix(BaseHostDataSource& data_source1,
+ BaseHostDataSource& data_source2) {
ConstHostPtr host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
ASSERT_FALSE(host);
// Add a host with a reservation for a prefix 2001:db8:1::/64.
- HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1),
- SubnetID(2), IOAddress("0.0.0.0")));
- new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
- IOAddress("2001:db8:1::"), 64));
- getCfgHosts()->add(new_host);
+ addHost6(data_source1, duids_[0], SubnetID(2), IOAddress("2001:db8:1::"), 64);
// Add another host having a reservation for prefix 2001:db8:1:0:6::/72.
- new_host.reset(new Host(duids_[1]->toText(), "duid", SubnetID(2),
- SubnetID(3), IOAddress::IPV4_ZERO_ADDRESS()));
- new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
- IOAddress("2001:db8:1:0:6::"), 72));
- getCfgHosts()->add(new_host);
+ addHost6(data_source2, duids_[1], SubnetID(3), IOAddress("2001:db8:1:0:6::"), 72);
+
CfgMgr::instance().commit();
// Retrieve first reservation.
host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
ASSERT_TRUE(host);
EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
- IOAddress("2001:db8:1::"), 64)));
+ IOAddress("2001:db8:1::"), 64)));
// Make sure the first reservation is not retrieved when the prefix
// length is incorrect.
@@ -246,7 +346,7 @@ TEST_F(HostMgrTest, get6ByPrefix) {
host = HostMgr::instance().get6(IOAddress("2001:db8:1:0:6::"), 72);
ASSERT_TRUE(host);
EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
- IOAddress("2001:db8:1:0:6::"), 72)));
+ IOAddress("2001:db8:1:0:6::"), 72)));
// Make sure the second reservation is not retrieved when the prefix
// length is incorrect.
@@ -254,4 +354,190 @@ TEST_F(HostMgrTest, get6ByPrefix) {
EXPECT_FALSE(host);
}
+/// This test verifies that HostMgr returns all reservations for the
+/// specified HW address. The reservations are defined in the server's
+/// configuration.
+TEST_F(HostMgrTest, getAll) {
+ testGetAll(*getCfgHosts(), *getCfgHosts());
+}
+
+// This test verifies that it is possible to gather all reservations for the
+// specified IPv4 address from the HostMgr. The reservations are specified in
+// the server's configuration.
+TEST_F(HostMgrTest, getAll4) {
+ testGetAll4(*getCfgHosts(), *getCfgHosts());
+}
+
+// This test verifies that it is possible to retrieve a reservation for the
+// particular host using HostMgr. The reservation is specified in the server's
+// configuration.
+TEST_F(HostMgrTest, get4) {
+ testGet4(*getCfgHosts());
+}
+
+// This test verifies that it is possible to retrieve IPv6 reservations for
+// the particular host using HostMgr. The reservation is specified in the
+// server's configuration.
+TEST_F(HostMgrTest, get6) {
+ testGet6(*getCfgHosts());
+}
+
+// This test verifies that it is possible to retrieve the reservation of the
+// particular IPv6 prefix using HostMgr.
+TEST_F(HostMgrTest, get6ByPrefix) {
+ testGet6ByPrefix(*getCfgHosts(), *getCfgHosts());
+}
+
+// The following tests require MySQL enabled.
+#if defined HAVE_MYSQL
+
+/// @brief Test fixture class for validating @c HostMgr using
+/// MySQL as alternate host data source.
+class MySQLHostMgrTest : public HostMgrTest {
+protected:
+
+ /// @brief Build MySQL schema for a test.
+ virtual void SetUp();
+
+ /// @brief Rollback and drop MySQL schema after the test.
+ virtual void TearDown();
+};
+
+void
+MySQLHostMgrTest::SetUp() {
+ HostMgrTest::SetUp();
+
+ // Ensure schema is the correct one.
+ test::destroyMySQLSchema();
+ test::createMySQLSchema();
+
+ // Connect to the database
+ try {
+ HostMgr::create(test::validMySQLConnectionString());
+ } catch (...) {
+ std::cerr << "*** ERROR: unable to open database. The test\n"
+ "*** environment is broken and must be fixed before\n"
+ "*** the MySQL tests will run correctly.\n"
+ "*** The reason for the problem is described in the\n"
+ "*** accompanying exception output.\n";
+ throw;
+ }
+}
+
+void
+MySQLHostMgrTest::TearDown() {
+ HostDataSourceFactory::getHostDataSourcePtr()->rollback();
+ HostDataSourceFactory::destroy();
+ test::destroyMySQLSchema();
+}
+
+// This test verifies that reservations for a particular client can
+// be retrieved from the confguration file and a database simultaneously.
+TEST_F(MySQLHostMgrTest, getAll) {
+ testGetAll(*getCfgHosts(), HostMgr::instance());
+}
+
+// This test verifies that IPv4 reservations for a particular client can
+// be retrieved from the configuration file and a database simulatneously.
+TEST_F(MySQLHostMgrTest, getAll4) {
+ testGetAll4(*getCfgHosts(), HostMgr::instance());
+}
+
+// This test verifies that the IPv4 reservation can be retrieved from a
+// database.
+TEST_F(MySQLHostMgrTest, get4) {
+ testGet4(HostMgr::instance());
+}
+
+// This test verifies that the IPv6 reservation can be retrieved from a
+// database.
+TEST_F(MySQLHostMgrTest, get6) {
+ testGet6(HostMgr::instance());
+}
+
+// This test verifies that the IPv6 prefix reservation can be retrieved
+// from a configuration file and a database.
+TEST_F(MySQLHostMgrTest, get6ByPrefix) {
+ testGet6ByPrefix(*getCfgHosts(), HostMgr::instance());
+}
+
+#endif
+
+
+// The following tests require PostgreSQL enabled.
+#if defined HAVE_PGSQL
+
+/// @brief Test fixture class for validating @c HostMgr using
+/// PostgreSQL as alternate host data source.
+class PostgreSQLHostMgrTest : public HostMgrTest {
+protected:
+
+ /// @brief Build PostgreSQL schema for a test.
+ virtual void SetUp();
+
+ /// @brief Rollback and drop PostgreSQL schema after the test.
+ virtual void TearDown();
+
+};
+
+void
+PostgreSQLHostMgrTest::SetUp() {
+ HostMgrTest::SetUp();
+
+ // Ensure schema is the correct one.
+ test::destroyPgSQLSchema();
+ test::createPgSQLSchema();
+
+ // Connect to the database
+ try {
+ HostMgr::create(test::validPgSQLConnectionString());
+ } catch (...) {
+ std::cerr << "*** ERROR: unable to open database. The test\n"
+ "*** environment is broken and must be fixed before\n"
+ "*** the PostgreSQL tests will run correctly.\n"
+ "*** The reason for the problem is described in the\n"
+ "*** accompanying exception output.\n";
+ throw;
+ }
+}
+
+void
+PostgreSQLHostMgrTest::TearDown() {
+ HostDataSourceFactory::getHostDataSourcePtr()->rollback();
+ HostDataSourceFactory::destroy();
+ test::destroyPgSQLSchema();
+}
+
+// This test verifies that reservations for a particular client can
+// be retrieved from the confguration file and a database simultaneously.
+TEST_F(PostgreSQLHostMgrTest, getAll) {
+ testGetAll(*getCfgHosts(), HostMgr::instance());
+}
+
+// This test verifies that IPv4 reservations for a particular client can
+// be retrieved from the configuration file and a database simulatneously.
+TEST_F(PostgreSQLHostMgrTest, getAll4) {
+ testGetAll4(*getCfgHosts(), HostMgr::instance());
+}
+
+// This test verifies that the IPv4 reservation can be retrieved from a
+// database.
+TEST_F(PostgreSQLHostMgrTest, get4) {
+ testGet4(HostMgr::instance());
+}
+
+// This test verifies that the IPv6 reservation can be retrieved from a
+// database.
+TEST_F(PostgreSQLHostMgrTest, get6) {
+ testGet6(HostMgr::instance());
+}
+
+// This test verifies that the IPv6 prefix reservation can be retrieved
+// from a configuration file and a database.
+TEST_F(PostgreSQLHostMgrTest, get6ByPrefix) {
+ testGet6ByPrefix(*getCfgHosts(), HostMgr::instance());
+}
+
+#endif
+
} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
index a01a54457c..f52f2c1bd3 100644
--- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
@@ -1883,6 +1883,16 @@ TEST_F(MemfileLeaseMgrTest, lease6ContainerIndexUpdate) {
}
}
+// Verifies that IPv4 lease statistics can be recalculated.
+TEST_F(MemfileLeaseMgrTest, recountLeaseStats4) {
+ startBackend(V4);
+ testRecountLeaseStats4();
+}
+// Verifies that IPv6 lease statistics can be recalculated.
+TEST_F(MemfileLeaseMgrTest, recountLeaseStats6) {
+ startBackend(V6);
+ testRecountLeaseStats6();
+}
}; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
index d5f9a846f8..60359afd89 100644
--- a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
+++ b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
@@ -63,8 +63,13 @@ public:
/// Rolls back all pending transactions. The deletion of myhdsptr_ will close
/// the database. Then reopen it and delete everything created by the test.
virtual ~MySqlHostDataSourceTest() {
- hdsptr_->rollback();
+ try {
+ hdsptr_->rollback();
+ } catch (...) {
+ // Rollback may fail if backend is in read only mode. That's ok.
+ }
HostDataSourceFactory::destroy();
+ hdsptr_.reset();
destroyMySQLSchema();
}
@@ -157,6 +162,9 @@ TEST(MySqlHostDataSource, OpenDatabase) {
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)),
DbInvalidTimeout);
+ EXPECT_THROW(HostDataSourceFactory::create(connectionString(
+ MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD,
+ VALID_TIMEOUT, INVALID_READONLY_DB)), DbInvalidReadOnly);
// Check for missing parameters
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
@@ -167,6 +175,8 @@ TEST(MySqlHostDataSource, OpenDatabase) {
destroyMySQLSchema();
}
+
+
/// @brief Check conversion functions
///
/// The server works using cltt and valid_filetime. In the database, the
@@ -208,6 +218,10 @@ TEST(MySqlConnection, checkTimeConversion) {
EXPECT_EQ(cltt, converted_cltt);
}
+TEST_F(MySqlHostDataSourceTest, testReadOnlyDatabase) {
+ testReadOnlyDatabase(MYSQL_VALID_TYPE);
+}
+
// Test verifies if a host reservation can be added and later retrieved by IPv4
// address. Host uses hw address as identifier.
TEST_F(MySqlHostDataSourceTest, basic4HWAddr) {
diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
index 33549b415a..b5433ab332 100644
--- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
@@ -477,4 +477,14 @@ TEST_F(MySqlLeaseMgrTest, deleteExpiredReclaimedLeases4) {
testDeleteExpiredReclaimedLeases4();
}
+// Verifies that IPv4 lease statistics can be recalculated.
+TEST_F(MySqlLeaseMgrTest, recountLeaseStats4) {
+ testRecountLeaseStats4();
+}
+
+// Verifies that IPv6 lease statistics can be recalculated.
+TEST_F(MySqlLeaseMgrTest, recountLeaseStats6) {
+ testRecountLeaseStats6();
+}
+
}; // Of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc
index 8f22257ddb..b5456bfb8e 100644
--- a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc
+++ b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc
@@ -64,8 +64,13 @@ public:
/// close the database. Then reopen it and delete everything created by
/// the test.
virtual ~PgSqlHostDataSourceTest() {
- hdsptr_->rollback();
+ try {
+ hdsptr_->rollback();
+ } catch (...) {
+ // Rollback may fail if backend is in read only mode. That's ok.
+ }
HostDataSourceFactory::destroy();
+ hdsptr_.reset();
destroyPgSQLSchema();
}
@@ -168,6 +173,12 @@ TEST(PgSqlHostDataSource, OpenDatabase) {
destroyPgSQLSchema();
}
+
+// This test verifies that database backend can operate in Read-Only mode.
+TEST_F(PgSqlHostDataSourceTest, testReadOnlyDatabase) {
+ testReadOnlyDatabase(PGSQL_VALID_TYPE);
+}
+
// Test verifies if a host reservation can be added and later retrieved by IPv4
// address. Host uses hw address as identifier.
TEST_F(PgSqlHostDataSourceTest, basic4HWAddr) {
diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc
index 9234881863..0a629d5579 100644
--- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc
@@ -402,4 +402,14 @@ TEST_F(PgSqlLeaseMgrTest, getExpiredLeases6) {
testGetExpiredLeases6();
}
+// Verifies that IPv4 lease statistics can be recalculated.
+TEST_F(PgSqlLeaseMgrTest, recountLeaseStats4) {
+ testRecountLeaseStats4();
+}
+
+// Verifies that IPv6 lease statistics can be recalculated.
+TEST_F(PgSqlLeaseMgrTest, recountLeaseStats6) {
+ testRecountLeaseStats6();
+}
+
}; // namespace
diff --git a/src/lib/dhcpsrv/testutils/Makefile.am b/src/lib/dhcpsrv/testutils/Makefile.am
index 7f62aa3775..de4b15a898 100644
--- a/src/lib/dhcpsrv/testutils/Makefile.am
+++ b/src/lib/dhcpsrv/testutils/Makefile.am
@@ -15,17 +15,7 @@ noinst_LTLIBRARIES = libdhcpsrvtest.la
libdhcpsrvtest_la_SOURCES = config_result_check.cc config_result_check.h
libdhcpsrvtest_la_SOURCES += dhcp4o6_test_ipc.cc dhcp4o6_test_ipc.h
-if HAVE_MYSQL
-libdhcpsrvtest_la_SOURCES += schema.cc schema.h
-else
-if HAVE_PGSQL
-libdhcpsrvtest_la_SOURCES += schema.cc schema.h
-else
-if HAVE_CQL
libdhcpsrvtest_la_SOURCES += schema.cc schema.h
-endif
-endif
-endif
if HAVE_MYSQL
libdhcpsrvtest_la_SOURCES += mysql_schema.cc mysql_schema.h
diff --git a/src/lib/dhcpsrv/testutils/schema.cc b/src/lib/dhcpsrv/testutils/schema.cc
index cefcbdae5c..b373363dc7 100644
--- a/src/lib/dhcpsrv/testutils/schema.cc
+++ b/src/lib/dhcpsrv/testutils/schema.cc
@@ -25,15 +25,19 @@ const char* INVALID_NAME = "name=invalidname";
const char* VALID_HOST = "host=localhost";
const char* INVALID_HOST = "host=invalidhost";
const char* VALID_USER = "user=keatest";
+const char* VALID_READONLY_USER = "user=keatest_readonly";
const char* INVALID_USER = "user=invaliduser";
const char* VALID_PASSWORD = "password=keatest";
const char* INVALID_PASSWORD = "password=invalid";
const char* VALID_TIMEOUT = "connect-timeout=10";
const char* INVALID_TIMEOUT_1 = "connect-timeout=foo";
const char* INVALID_TIMEOUT_2 = "connect-timeout=-17";
+const char* VALID_READONLY_DB = "readonly=true";
+const char* INVALID_READONLY_DB = "readonly=5";
string connectionString(const char* type, const char* name, const char* host,
- const char* user, const char* password, const char* timeout) {
+ const char* user, const char* password, const char* timeout,
+ const char* readonly_db = NULL) {
const string space = " ";
string result = "";
@@ -75,6 +79,13 @@ string connectionString(const char* type, const char* name, const char* host,
result += string(timeout);
}
+ if (readonly_db != NULL) {
+ if (! result.empty()) {
+ result += space;
+ }
+ result += string(readonly_db);
+ }
+
return (result);
}
diff --git a/src/lib/dhcpsrv/testutils/schema.h b/src/lib/dhcpsrv/testutils/schema.h
index 5a0471512f..14d04abb6e 100644
--- a/src/lib/dhcpsrv/testutils/schema.h
+++ b/src/lib/dhcpsrv/testutils/schema.h
@@ -21,12 +21,16 @@ extern const char* INVALID_NAME;
extern const char* VALID_HOST;
extern const char* INVALID_HOST;
extern const char* VALID_USER;
+extern const char* VALID_READONLY_USER;
extern const char* INVALID_USER;
extern const char* VALID_PASSWORD;
extern const char* INVALID_PASSWORD;
extern const char* VALID_TIMEOUT;
extern const char* INVALID_TIMEOUT_1;
extern const char* INVALID_TIMEOUT_2;
+extern const char* VALID_READONLY_DB;
+extern const char* INVALID_READONLY_DB;
+
/// @brief Given a combination of strings above, produce a connection string.
///
/// @param type type of the database
@@ -35,10 +39,12 @@ extern const char* INVALID_TIMEOUT_2;
/// @param user username used to authenticate during connection attempt
/// @param password password used to authenticate during connection attempt
/// @param timeout timeout used during connection attempt
+/// @param readonly_db specifies if database is read only
/// @return string containing all specified parameters
std::string connectionString(const char* type, const char* name = NULL,
const char* host = NULL, const char* user = NULL,
- const char* password = NULL, const char* timeout = NULL);
+ const char* password = NULL, const char* timeout = NULL,
+ const char* readonly_db = NULL);
};
};
};
diff --git a/src/lib/eval/eval_context.cc b/src/lib/eval/eval_context.cc
index 4ede1dbc42..8cd13653b5 100644
--- a/src/lib/eval/eval_context.cc
+++ b/src/lib/eval/eval_context.cc
@@ -139,7 +139,7 @@ EvalContext::convertUint32(const std::string& number,
} catch (const boost::bad_lexical_cast &) {
error(loc, "Invalid value in " + number);
}
- if (n >= std::numeric_limits<uint32_t>::max()) {
+ if (n > std::numeric_limits<uint32_t>::max()) {
error(loc, "Invalid value in "
+ number + ". Allowed range: 0..4294967295");
}
@@ -147,6 +147,17 @@ EvalContext::convertUint32(const std::string& number,
return (static_cast<uint32_t>(n));
}
+std::string
+EvalContext::fromUint32(const uint32_t integer) {
+ std::string tmp(4, 0);
+ tmp[0] = (integer >> 24) & 0xff;
+ tmp[1] = (integer >> 16) & 0xff;
+ tmp[2] = (integer >> 8) & 0xff;
+ tmp[3] = integer & 0xff;
+
+ return (tmp);
+}
+
void
EvalContext::fatal (const std::string& what)
{
diff --git a/src/lib/eval/eval_context.h b/src/lib/eval/eval_context.h
index 4984554ec4..385f1a7aeb 100644
--- a/src/lib/eval/eval_context.h
+++ b/src/lib/eval/eval_context.h
@@ -69,13 +69,13 @@ public:
///
/// @param loc location within the parsed file when experienced a problem.
/// @param what string explaining the nature of the error.
- void error(const isc::eval::location& loc, const std::string& what);
+ static void error(const isc::eval::location& loc, const std::string& what);
/// @brief Error handler
///
/// This is a simplified error reporting tool for possible future
/// cases when the EvalParser is not able to handle the packet.
- void error(const std::string& what);
+ static void error(const std::string& what);
/// @brief Fatal error handler
///
@@ -103,12 +103,14 @@ public:
/// @brief Attempts to convert string to unsigned 32bit integer
///
+ /// For reverse conversion, see @ref fromUint32
+ ///
/// @param number string to be converted
/// @param loc the location of the token
/// @return the integer value
/// @throw EvalParseError if conversion fails or the value is out of range.
- uint32_t convertUint32(const std::string& number,
- const isc::eval::location& loc);
+ static uint32_t convertUint32(const std::string& number,
+ const isc::eval::location& loc);
/// @brief Attempts to convert string to unsigned 8bit integer
///
@@ -116,8 +118,8 @@ public:
/// @param loc the location of the token
/// @return the integer value
/// @throw EvalParseError if conversion fails or the value is out of range.
- uint8_t convertUint8(const std::string& number,
- const isc::eval::location& loc);
+ static uint8_t convertUint8(const std::string& number,
+ const isc::eval::location& loc);
/// @brief Nest level conversion
///
@@ -127,7 +129,17 @@ public:
/// @throw calls the syntax error function if the value is not in
/// the range 0..31
uint8_t convertNestLevelNumber(const std::string& nest_level,
- const isc::eval::location& loc);
+ const isc::eval::location& loc);
+
+ /// @brief Converts integer to string representation
+ ///
+ /// The integer is coded as a 4 byte long string in network order, e.g.
+ /// 6 is represented as 00000006. For reverse conversion, see
+ /// @ref convertUint32.
+ ///
+ /// @param integer value to be converted
+ /// @return 4 byte long string that encodes the value.
+ static std::string fromUint32(const uint32_t integer);
/// @brief Returns the universe (v4 or v6)
///
diff --git a/src/lib/eval/lexer.cc b/src/lib/eval/lexer.cc
index 69757ba1b3..af6de6d4bb 100644
--- a/src/lib/eval/lexer.cc
+++ b/src/lib/eval/lexer.cc
@@ -745,11 +745,11 @@ int yy_flex_debug = 1;
static yyconst flex_int16_t yy_rule_linenum[51] =
{ 0,
- 82, 86, 92, 102, 108, 122, 129, 143, 144, 145,
- 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
- 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
- 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
- 176, 177, 178, 179, 180, 181, 182, 183, 184, 185
+ 82, 86, 92, 102, 108, 126, 133, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189
} ;
static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
@@ -1286,7 +1286,11 @@ YY_RULE_SETUP
std::string tmp(yytext);
try {
- static_cast<void>(boost::lexical_cast<int>(tmp));
+ // In substring we want to use negative values (e.g. -1).
+ // In enterprise-id we need to use values up to 0xffffffff.
+ // To cover both of those use cases, we need at least
+ // int64_t.
+ static_cast<void>(boost::lexical_cast<int64_t>(tmp));
} catch (const boost::bad_lexical_cast &) {
driver.error(loc, "Failed to convert " + tmp + " to an integer.");
}
@@ -1298,7 +1302,7 @@ YY_RULE_SETUP
case 6:
/* rule 6 can match eol */
YY_RULE_SETUP
-#line 122 "lexer.ll"
+#line 126 "lexer.ll"
{
// This string specifies option name starting with a letter
// and further containing letters, digits, hyphens and
@@ -1308,7 +1312,7 @@ YY_RULE_SETUP
YY_BREAK
case 7:
YY_RULE_SETUP
-#line 129 "lexer.ll"
+#line 133 "lexer.ll"
{
// IPv4 or IPv6 address
std::string tmp(yytext);
@@ -1325,229 +1329,229 @@ YY_RULE_SETUP
YY_BREAK
case 8:
YY_RULE_SETUP
-#line 143 "lexer.ll"
+#line 147 "lexer.ll"
return isc::eval::EvalParser::make_EQUAL(loc);
YY_BREAK
case 9:
YY_RULE_SETUP
-#line 144 "lexer.ll"
+#line 148 "lexer.ll"
return isc::eval::EvalParser::make_OPTION(loc);
YY_BREAK
case 10:
YY_RULE_SETUP
-#line 145 "lexer.ll"
+#line 149 "lexer.ll"
return isc::eval::EvalParser::make_RELAY4(loc);
YY_BREAK
case 11:
YY_RULE_SETUP
-#line 146 "lexer.ll"
+#line 150 "lexer.ll"
return isc::eval::EvalParser::make_RELAY6(loc);
YY_BREAK
case 12:
YY_RULE_SETUP
-#line 147 "lexer.ll"
+#line 151 "lexer.ll"
return isc::eval::EvalParser::make_PEERADDR(loc);
YY_BREAK
case 13:
YY_RULE_SETUP
-#line 148 "lexer.ll"
+#line 152 "lexer.ll"
return isc::eval::EvalParser::make_LINKADDR(loc);
YY_BREAK
case 14:
YY_RULE_SETUP
-#line 149 "lexer.ll"
+#line 153 "lexer.ll"
return isc::eval::EvalParser::make_TEXT(loc);
YY_BREAK
case 15:
YY_RULE_SETUP
-#line 150 "lexer.ll"
+#line 154 "lexer.ll"
return isc::eval::EvalParser::make_HEX(loc);
YY_BREAK
case 16:
YY_RULE_SETUP
-#line 151 "lexer.ll"
+#line 155 "lexer.ll"
return isc::eval::EvalParser::make_EXISTS(loc);
YY_BREAK
case 17:
YY_RULE_SETUP
-#line 152 "lexer.ll"
+#line 156 "lexer.ll"
return isc::eval::EvalParser::make_PKT(loc);
YY_BREAK
case 18:
YY_RULE_SETUP
-#line 153 "lexer.ll"
+#line 157 "lexer.ll"
return isc::eval::EvalParser::make_IFACE(loc);
YY_BREAK
case 19:
YY_RULE_SETUP
-#line 154 "lexer.ll"
+#line 158 "lexer.ll"
return isc::eval::EvalParser::make_SRC(loc);
YY_BREAK
case 20:
YY_RULE_SETUP
-#line 155 "lexer.ll"
+#line 159 "lexer.ll"
return isc::eval::EvalParser::make_DST(loc);
YY_BREAK
case 21:
YY_RULE_SETUP
-#line 156 "lexer.ll"
+#line 160 "lexer.ll"
return isc::eval::EvalParser::make_LEN(loc);
YY_BREAK
case 22:
YY_RULE_SETUP
-#line 157 "lexer.ll"
+#line 161 "lexer.ll"
return isc::eval::EvalParser::make_PKT4(loc);
YY_BREAK
case 23:
YY_RULE_SETUP
-#line 158 "lexer.ll"
+#line 162 "lexer.ll"
return isc::eval::EvalParser::make_CHADDR(loc);
YY_BREAK
case 24:
YY_RULE_SETUP
-#line 159 "lexer.ll"
+#line 163 "lexer.ll"
return isc::eval::EvalParser::make_HLEN(loc);
YY_BREAK
case 25:
YY_RULE_SETUP
-#line 160 "lexer.ll"
+#line 164 "lexer.ll"
return isc::eval::EvalParser::make_HTYPE(loc);
YY_BREAK
case 26:
YY_RULE_SETUP
-#line 161 "lexer.ll"
+#line 165 "lexer.ll"
return isc::eval::EvalParser::make_CIADDR(loc);
YY_BREAK
case 27:
YY_RULE_SETUP
-#line 162 "lexer.ll"
+#line 166 "lexer.ll"
return isc::eval::EvalParser::make_GIADDR(loc);
YY_BREAK
case 28:
YY_RULE_SETUP
-#line 163 "lexer.ll"
+#line 167 "lexer.ll"
return isc::eval::EvalParser::make_YIADDR(loc);
YY_BREAK
case 29:
YY_RULE_SETUP
-#line 164 "lexer.ll"
+#line 168 "lexer.ll"
return isc::eval::EvalParser::make_SIADDR(loc);
YY_BREAK
case 30:
YY_RULE_SETUP
-#line 165 "lexer.ll"
+#line 169 "lexer.ll"
return isc::eval::EvalParser::make_PKT6(loc);
YY_BREAK
case 31:
YY_RULE_SETUP
-#line 166 "lexer.ll"
+#line 170 "lexer.ll"
return isc::eval::EvalParser::make_MSGTYPE(loc);
YY_BREAK
case 32:
YY_RULE_SETUP
-#line 167 "lexer.ll"
+#line 171 "lexer.ll"
return isc::eval::EvalParser::make_TRANSID(loc);
YY_BREAK
case 33:
YY_RULE_SETUP
-#line 168 "lexer.ll"
+#line 172 "lexer.ll"
return isc::eval::EvalParser::make_VENDOR(loc);
YY_BREAK
case 34:
YY_RULE_SETUP
-#line 169 "lexer.ll"
+#line 173 "lexer.ll"
return isc::eval::EvalParser::make_VENDOR_CLASS(loc);
YY_BREAK
case 35:
YY_RULE_SETUP
-#line 170 "lexer.ll"
+#line 174 "lexer.ll"
return isc::eval::EvalParser::make_DATA(loc);
YY_BREAK
case 36:
YY_RULE_SETUP
-#line 171 "lexer.ll"
+#line 175 "lexer.ll"
return isc::eval::EvalParser::make_ENTERPRISE(loc);
YY_BREAK
case 37:
YY_RULE_SETUP
-#line 172 "lexer.ll"
+#line 176 "lexer.ll"
return isc::eval::EvalParser::make_SUBSTRING(loc);
YY_BREAK
case 38:
YY_RULE_SETUP
-#line 173 "lexer.ll"
+#line 177 "lexer.ll"
return isc::eval::EvalParser::make_ALL(loc);
YY_BREAK
case 39:
YY_RULE_SETUP
-#line 174 "lexer.ll"
+#line 178 "lexer.ll"
return isc::eval::EvalParser::make_CONCAT(loc);
YY_BREAK
case 40:
YY_RULE_SETUP
-#line 175 "lexer.ll"
+#line 179 "lexer.ll"
return isc::eval::EvalParser::make_NOT(loc);
YY_BREAK
case 41:
YY_RULE_SETUP
-#line 176 "lexer.ll"
+#line 180 "lexer.ll"
return isc::eval::EvalParser::make_AND(loc);
YY_BREAK
case 42:
YY_RULE_SETUP
-#line 177 "lexer.ll"
+#line 181 "lexer.ll"
return isc::eval::EvalParser::make_OR(loc);
YY_BREAK
case 43:
YY_RULE_SETUP
-#line 178 "lexer.ll"
+#line 182 "lexer.ll"
return isc::eval::EvalParser::make_DOT(loc);
YY_BREAK
case 44:
YY_RULE_SETUP
-#line 179 "lexer.ll"
+#line 183 "lexer.ll"
return isc::eval::EvalParser::make_LPAREN(loc);
YY_BREAK
case 45:
YY_RULE_SETUP
-#line 180 "lexer.ll"
+#line 184 "lexer.ll"
return isc::eval::EvalParser::make_RPAREN(loc);
YY_BREAK
case 46:
YY_RULE_SETUP
-#line 181 "lexer.ll"
+#line 185 "lexer.ll"
return isc::eval::EvalParser::make_LBRACKET(loc);
YY_BREAK
case 47:
YY_RULE_SETUP
-#line 182 "lexer.ll"
+#line 186 "lexer.ll"
return isc::eval::EvalParser::make_RBRACKET(loc);
YY_BREAK
case 48:
YY_RULE_SETUP
-#line 183 "lexer.ll"
+#line 187 "lexer.ll"
return isc::eval::EvalParser::make_COMA(loc);
YY_BREAK
case 49:
YY_RULE_SETUP
-#line 184 "lexer.ll"
+#line 188 "lexer.ll"
return isc::eval::EvalParser::make_ANY(loc);
YY_BREAK
case 50:
YY_RULE_SETUP
-#line 185 "lexer.ll"
+#line 189 "lexer.ll"
driver.error (loc, "Invalid character: " + std::string(yytext));
YY_BREAK
case YY_STATE_EOF(INITIAL):
-#line 186 "lexer.ll"
+#line 190 "lexer.ll"
return isc::eval::EvalParser::make_END(loc);
YY_BREAK
case 51:
YY_RULE_SETUP
-#line 187 "lexer.ll"
+#line 191 "lexer.ll"
ECHO;
YY_BREAK
-#line 1551 "lexer.cc"
+#line 1555 "lexer.cc"
case YY_END_OF_BUFFER:
{
@@ -2630,7 +2634,7 @@ void yyfree (void * ptr )
/* %ok-for-header */
-#line 187 "lexer.ll"
+#line 191 "lexer.ll"
diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll
index fdefcecd63..f7bd53593a 100644
--- a/src/lib/eval/lexer.ll
+++ b/src/lib/eval/lexer.ll
@@ -110,7 +110,11 @@ addr6 [0-9a-fA-F]*\:[0-9a-fA-F]*\:[0-9a-fA-F:.]*
std::string tmp(yytext);
try {
- static_cast<void>(boost::lexical_cast<int>(tmp));
+ // In substring we want to use negative values (e.g. -1).
+ // In enterprise-id we need to use values up to 0xffffffff.
+ // To cover both of those use cases, we need at least
+ // int64_t.
+ static_cast<void>(boost::lexical_cast<int64_t>(tmp));
} catch (const boost::bad_lexical_cast &) {
driver.error(loc, "Failed to convert " + tmp + " to an integer.");
}
diff --git a/src/lib/eval/parser.cc b/src/lib/eval/parser.cc
index 8504c3d485..4631b44a7d 100644
--- a/src/lib/eval/parser.cc
+++ b/src/lib/eval/parser.cc
@@ -251,23 +251,23 @@ namespace isc { namespace eval {
{
switch (that.type_get ())
{
- case 55: // option_repr_type
+ case 56: // option_repr_type
value.move< TokenOption::RepresentationType > (that.value);
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
value.move< TokenPkt4::FieldType > (that.value);
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
value.move< TokenPkt6::FieldType > (that.value);
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
value.move< TokenPkt::MetadataType > (that.value);
break;
- case 61: // relay6_field
+ case 62: // relay6_field
value.move< TokenRelay6Field::FieldType > (that.value);
break;
@@ -279,15 +279,16 @@ namespace isc { namespace eval {
value.move< std::string > (that.value);
break;
- case 54: // option_code
+ case 55: // option_code
value.move< uint16_t > (that.value);
break;
- case 58: // enterprise_id
+ case 54: // integer_expr
+ case 59: // enterprise_id
value.move< uint32_t > (that.value);
break;
- case 56: // nest_level
+ case 57: // nest_level
value.move< uint8_t > (that.value);
break;
@@ -306,23 +307,23 @@ namespace isc { namespace eval {
state = that.state;
switch (that.type_get ())
{
- case 55: // option_repr_type
+ case 56: // option_repr_type
value.copy< TokenOption::RepresentationType > (that.value);
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
value.copy< TokenPkt4::FieldType > (that.value);
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
value.copy< TokenPkt6::FieldType > (that.value);
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
value.copy< TokenPkt::MetadataType > (that.value);
break;
- case 61: // relay6_field
+ case 62: // relay6_field
value.copy< TokenRelay6Field::FieldType > (that.value);
break;
@@ -334,15 +335,16 @@ namespace isc { namespace eval {
value.copy< std::string > (that.value);
break;
- case 54: // option_code
+ case 55: // option_code
value.copy< uint16_t > (that.value);
break;
- case 58: // enterprise_id
+ case 54: // integer_expr
+ case 59: // enterprise_id
value.copy< uint32_t > (that.value);
break;
- case 56: // nest_level
+ case 57: // nest_level
value.copy< uint8_t > (that.value);
break;
@@ -384,93 +386,100 @@ namespace isc { namespace eval {
{
case 45: // "constant string"
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
-#line 390 "parser.cc" // lalr1.cc:636
+#line 392 "parser.cc" // lalr1.cc:636
break;
case 46: // "integer"
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
-#line 397 "parser.cc" // lalr1.cc:636
+#line 399 "parser.cc" // lalr1.cc:636
break;
case 47: // "constant hexstring"
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
-#line 404 "parser.cc" // lalr1.cc:636
+#line 406 "parser.cc" // lalr1.cc:636
break;
case 48: // "option name"
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
-#line 411 "parser.cc" // lalr1.cc:636
+#line 413 "parser.cc" // lalr1.cc:636
break;
case 49: // "ip address"
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
-#line 418 "parser.cc" // lalr1.cc:636
+#line 420 "parser.cc" // lalr1.cc:636
break;
- case 54: // option_code
+ case 54: // integer_expr
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
+ { yyoutput << yysym.value.template as< uint32_t > (); }
+#line 427 "parser.cc" // lalr1.cc:636
+ break;
+
+ case 55: // option_code
+
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< uint16_t > (); }
-#line 425 "parser.cc" // lalr1.cc:636
+#line 434 "parser.cc" // lalr1.cc:636
break;
- case 55: // option_repr_type
+ case 56: // option_repr_type
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
-#line 432 "parser.cc" // lalr1.cc:636
+#line 441 "parser.cc" // lalr1.cc:636
break;
- case 56: // nest_level
+ case 57: // nest_level
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< uint8_t > (); }
-#line 439 "parser.cc" // lalr1.cc:636
+#line 448 "parser.cc" // lalr1.cc:636
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< TokenPkt::MetadataType > (); }
-#line 446 "parser.cc" // lalr1.cc:636
+#line 455 "parser.cc" // lalr1.cc:636
break;
- case 58: // enterprise_id
+ case 59: // enterprise_id
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< uint32_t > (); }
-#line 453 "parser.cc" // lalr1.cc:636
+#line 462 "parser.cc" // lalr1.cc:636
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< TokenPkt4::FieldType > (); }
-#line 460 "parser.cc" // lalr1.cc:636
+#line 469 "parser.cc" // lalr1.cc:636
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< TokenPkt6::FieldType > (); }
-#line 467 "parser.cc" // lalr1.cc:636
+#line 476 "parser.cc" // lalr1.cc:636
break;
- case 61: // relay6_field
+ case 62: // relay6_field
-#line 103 "parser.yy" // lalr1.cc:636
+#line 104 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< TokenRelay6Field::FieldType > (); }
-#line 474 "parser.cc" // lalr1.cc:636
+#line 483 "parser.cc" // lalr1.cc:636
break;
@@ -670,23 +679,23 @@ namespace isc { namespace eval {
when using variants. */
switch (yyr1_[yyn])
{
- case 55: // option_repr_type
+ case 56: // option_repr_type
yylhs.value.build< TokenOption::RepresentationType > ();
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
yylhs.value.build< TokenPkt4::FieldType > ();
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
yylhs.value.build< TokenPkt6::FieldType > ();
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
yylhs.value.build< TokenPkt::MetadataType > ();
break;
- case 61: // relay6_field
+ case 62: // relay6_field
yylhs.value.build< TokenRelay6Field::FieldType > ();
break;
@@ -698,15 +707,16 @@ namespace isc { namespace eval {
yylhs.value.build< std::string > ();
break;
- case 54: // option_code
+ case 55: // option_code
yylhs.value.build< uint16_t > ();
break;
- case 58: // enterprise_id
+ case 54: // integer_expr
+ case 59: // enterprise_id
yylhs.value.build< uint32_t > ();
break;
- case 56: // nest_level
+ case 57: // nest_level
yylhs.value.build< uint8_t > ();
break;
@@ -728,52 +738,52 @@ namespace isc { namespace eval {
switch (yyn)
{
case 4:
-#line 117 "parser.yy" // lalr1.cc:859
+#line 118 "parser.yy" // lalr1.cc:859
{
TokenPtr neg(new TokenNot());
ctx.expression.push_back(neg);
}
-#line 737 "parser.cc" // lalr1.cc:859
+#line 747 "parser.cc" // lalr1.cc:859
break;
case 5:
-#line 122 "parser.yy" // lalr1.cc:859
+#line 123 "parser.yy" // lalr1.cc:859
{
TokenPtr neg(new TokenAnd());
ctx.expression.push_back(neg);
}
-#line 746 "parser.cc" // lalr1.cc:859
+#line 756 "parser.cc" // lalr1.cc:859
break;
case 6:
-#line 127 "parser.yy" // lalr1.cc:859
+#line 128 "parser.yy" // lalr1.cc:859
{
TokenPtr neg(new TokenOr());
ctx.expression.push_back(neg);
}
-#line 755 "parser.cc" // lalr1.cc:859
+#line 765 "parser.cc" // lalr1.cc:859
break;
case 7:
-#line 132 "parser.yy" // lalr1.cc:859
+#line 133 "parser.yy" // lalr1.cc:859
{
TokenPtr eq(new TokenEqual());
ctx.expression.push_back(eq);
}
-#line 764 "parser.cc" // lalr1.cc:859
+#line 774 "parser.cc" // lalr1.cc:859
break;
case 8:
-#line 137 "parser.yy" // lalr1.cc:859
+#line 138 "parser.yy" // lalr1.cc:859
{
TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS));
ctx.expression.push_back(opt);
}
-#line 773 "parser.cc" // lalr1.cc:859
+#line 783 "parser.cc" // lalr1.cc:859
break;
case 9:
-#line 142 "parser.yy" // lalr1.cc:859
+#line 143 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V4:
@@ -793,11 +803,11 @@ namespace isc { namespace eval {
error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
}
}
-#line 797 "parser.cc" // lalr1.cc:859
+#line 807 "parser.cc" // lalr1.cc:859
break;
case 10:
-#line 162 "parser.yy" // lalr1.cc:859
+#line 163 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V6:
@@ -811,11 +821,11 @@ namespace isc { namespace eval {
error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
}
}
-#line 815 "parser.cc" // lalr1.cc:859
+#line 825 "parser.cc" // lalr1.cc:859
break;
case 11:
-#line 176 "parser.yy" // lalr1.cc:859
+#line 177 "parser.yy" // lalr1.cc:859
{
// Expression: vendor-class[1234].exists
//
@@ -824,11 +834,11 @@ namespace isc { namespace eval {
TokenPtr exist(new TokenVendorClass(ctx.getUniverse(), yystack_[3].value.as< uint32_t > (), TokenOption::EXISTS));
ctx.expression.push_back(exist);
}
-#line 828 "parser.cc" // lalr1.cc:859
+#line 838 "parser.cc" // lalr1.cc:859
break;
case 12:
-#line 185 "parser.yy" // lalr1.cc:859
+#line 186 "parser.yy" // lalr1.cc:859
{
// Expression: vendor[1234].exists
//
@@ -837,11 +847,11 @@ namespace isc { namespace eval {
TokenPtr exist(new TokenVendor(ctx.getUniverse(), yystack_[3].value.as< uint32_t > (), TokenOption::EXISTS));
ctx.expression.push_back(exist);
}
-#line 841 "parser.cc" // lalr1.cc:859
+#line 851 "parser.cc" // lalr1.cc:859
break;
case 13:
-#line 194 "parser.yy" // lalr1.cc:859
+#line 195 "parser.yy" // lalr1.cc:859
{
// Expression vendor[1234].option[123].exists
//
@@ -851,47 +861,47 @@ namespace isc { namespace eval {
TokenPtr exist(new TokenVendor(ctx.getUniverse(), yystack_[8].value.as< uint32_t > (), TokenOption::EXISTS, yystack_[3].value.as< uint16_t > ()));
ctx.expression.push_back(exist);
}
-#line 855 "parser.cc" // lalr1.cc:859
+#line 865 "parser.cc" // lalr1.cc:859
break;
case 14:
-#line 206 "parser.yy" // lalr1.cc:859
+#line 207 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
-#line 864 "parser.cc" // lalr1.cc:859
+#line 874 "parser.cc" // lalr1.cc:859
break;
case 15:
-#line 211 "parser.yy" // lalr1.cc:859
+#line 212 "parser.yy" // lalr1.cc:859
{
TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(hex);
}
-#line 873 "parser.cc" // lalr1.cc:859
+#line 883 "parser.cc" // lalr1.cc:859
break;
case 16:
-#line 216 "parser.yy" // lalr1.cc:859
+#line 217 "parser.yy" // lalr1.cc:859
{
TokenPtr ip(new TokenIpAddress(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(ip);
}
-#line 882 "parser.cc" // lalr1.cc:859
+#line 892 "parser.cc" // lalr1.cc:859
break;
case 17:
-#line 221 "parser.yy" // lalr1.cc:859
+#line 222 "parser.yy" // lalr1.cc:859
{
TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
ctx.expression.push_back(opt);
}
-#line 891 "parser.cc" // lalr1.cc:859
+#line 901 "parser.cc" // lalr1.cc:859
break;
case 18:
-#line 226 "parser.yy" // lalr1.cc:859
+#line 227 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V4:
@@ -911,11 +921,11 @@ namespace isc { namespace eval {
error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
}
}
-#line 915 "parser.cc" // lalr1.cc:859
+#line 925 "parser.cc" // lalr1.cc:859
break;
case 19:
-#line 247 "parser.yy" // lalr1.cc:859
+#line 248 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V6:
@@ -929,20 +939,20 @@ namespace isc { namespace eval {
error(yystack_[10].location, "relay6 can only be used in DHCPv6.");
}
}
-#line 933 "parser.cc" // lalr1.cc:859
+#line 943 "parser.cc" // lalr1.cc:859
break;
case 20:
-#line 262 "parser.yy" // lalr1.cc:859
+#line 263 "parser.yy" // lalr1.cc:859
{
TokenPtr pkt_metadata(new TokenPkt(yystack_[0].value.as< TokenPkt::MetadataType > ()));
ctx.expression.push_back(pkt_metadata);
}
-#line 942 "parser.cc" // lalr1.cc:859
+#line 952 "parser.cc" // lalr1.cc:859
break;
case 21:
-#line 267 "parser.yy" // lalr1.cc:859
+#line 268 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V4:
@@ -956,11 +966,11 @@ namespace isc { namespace eval {
error(yystack_[2].location, "pkt4 can only be used in DHCPv4.");
}
}
-#line 960 "parser.cc" // lalr1.cc:859
+#line 970 "parser.cc" // lalr1.cc:859
break;
case 22:
-#line 281 "parser.yy" // lalr1.cc:859
+#line 282 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V6:
@@ -974,11 +984,11 @@ namespace isc { namespace eval {
error(yystack_[2].location, "pkt6 can only be used in DHCPv6.");
}
}
-#line 978 "parser.cc" // lalr1.cc:859
+#line 988 "parser.cc" // lalr1.cc:859
break;
case 23:
-#line 295 "parser.yy" // lalr1.cc:859
+#line 296 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V6:
@@ -992,29 +1002,29 @@ namespace isc { namespace eval {
error(yystack_[5].location, "relay6 can only be used in DHCPv6.");
}
}
-#line 996 "parser.cc" // lalr1.cc:859
+#line 1006 "parser.cc" // lalr1.cc:859
break;
case 24:
-#line 310 "parser.yy" // lalr1.cc:859
+#line 311 "parser.yy" // lalr1.cc:859
{
TokenPtr sub(new TokenSubstring());
ctx.expression.push_back(sub);
}
-#line 1005 "parser.cc" // lalr1.cc:859
+#line 1015 "parser.cc" // lalr1.cc:859
break;
case 25:
-#line 315 "parser.yy" // lalr1.cc:859
+#line 316 "parser.yy" // lalr1.cc:859
{
TokenPtr conc(new TokenConcat());
ctx.expression.push_back(conc);
}
-#line 1014 "parser.cc" // lalr1.cc:859
+#line 1024 "parser.cc" // lalr1.cc:859
break;
case 26:
-#line 320 "parser.yy" // lalr1.cc:859
+#line 321 "parser.yy" // lalr1.cc:859
{
// expression: vendor.enterprise
//
@@ -1023,11 +1033,11 @@ namespace isc { namespace eval {
TokenPtr vendor(new TokenVendor(ctx.getUniverse(), 0, TokenVendor::ENTERPRISE_ID));
ctx.expression.push_back(vendor);
}
-#line 1027 "parser.cc" // lalr1.cc:859
+#line 1037 "parser.cc" // lalr1.cc:859
break;
case 27:
-#line 329 "parser.yy" // lalr1.cc:859
+#line 330 "parser.yy" // lalr1.cc:859
{
// expression: vendor-class.enterprise
//
@@ -1037,11 +1047,11 @@ namespace isc { namespace eval {
TokenVendor::ENTERPRISE_ID));
ctx.expression.push_back(vendor);
}
-#line 1041 "parser.cc" // lalr1.cc:859
+#line 1051 "parser.cc" // lalr1.cc:859
break;
case 28:
-#line 339 "parser.yy" // lalr1.cc:859
+#line 340 "parser.yy" // lalr1.cc:859
{
// This token will search for vendor option with
// specified enterprise-id. If found, will search
@@ -1050,11 +1060,11 @@ namespace isc { namespace eval {
TokenPtr opt(new TokenVendor(ctx.getUniverse(), yystack_[8].value.as< uint32_t > (), yystack_[0].value.as< TokenOption::RepresentationType > (), yystack_[3].value.as< uint16_t > ()));
ctx.expression.push_back(opt);
}
-#line 1054 "parser.cc" // lalr1.cc:859
+#line 1064 "parser.cc" // lalr1.cc:859
break;
case 29:
-#line 348 "parser.yy" // lalr1.cc:859
+#line 349 "parser.yy" // lalr1.cc:859
{
// expression: vendor-class[1234].data
//
@@ -1067,11 +1077,11 @@ namespace isc { namespace eval {
TokenVendor::DATA, 0));
ctx.expression.push_back(vendor_class);
}
-#line 1071 "parser.cc" // lalr1.cc:859
+#line 1081 "parser.cc" // lalr1.cc:859
break;
case 30:
-#line 361 "parser.yy" // lalr1.cc:859
+#line 362 "parser.yy" // lalr1.cc:859
{
// expression: vendor-class[1234].data[5]
//
@@ -1084,214 +1094,247 @@ namespace isc { namespace eval {
TokenVendor::DATA, index));
ctx.expression.push_back(vendor_class);
}
-#line 1088 "parser.cc" // lalr1.cc:859
+#line 1098 "parser.cc" // lalr1.cc:859
break;
case 31:
-#line 376 "parser.yy" // lalr1.cc:859
+#line 375 "parser.yy" // lalr1.cc:859
+ {
+ TokenPtr integer(new TokenInteger(yystack_[0].value.as< uint32_t > ()));
+ ctx.expression.push_back(integer);
+ }
+#line 1107 "parser.cc" // lalr1.cc:859
+ break;
+
+ case 32:
+#line 382 "parser.yy" // lalr1.cc:859
+ {
+ yylhs.value.as< uint32_t > () = ctx.convertUint32(yystack_[0].value.as< std::string > (), yystack_[0].location);
+ }
+#line 1115 "parser.cc" // lalr1.cc:859
+ break;
+
+ case 33:
+#line 388 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
-#line 1096 "parser.cc" // lalr1.cc:859
+#line 1123 "parser.cc" // lalr1.cc:859
break;
- case 32:
-#line 380 "parser.yy" // lalr1.cc:859
+ case 34:
+#line 392 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
-#line 1104 "parser.cc" // lalr1.cc:859
+#line 1131 "parser.cc" // lalr1.cc:859
break;
- case 33:
-#line 386 "parser.yy" // lalr1.cc:859
+ case 35:
+#line 398 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
}
-#line 1112 "parser.cc" // lalr1.cc:859
+#line 1139 "parser.cc" // lalr1.cc:859
break;
- case 34:
-#line 390 "parser.yy" // lalr1.cc:859
+ case 36:
+#line 402 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
}
-#line 1120 "parser.cc" // lalr1.cc:859
+#line 1147 "parser.cc" // lalr1.cc:859
break;
- case 35:
-#line 396 "parser.yy" // lalr1.cc:859
+ case 37:
+#line 408 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< uint8_t > () = ctx.convertNestLevelNumber(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
-#line 1128 "parser.cc" // lalr1.cc:859
+#line 1155 "parser.cc" // lalr1.cc:859
break;
- case 36:
-#line 405 "parser.yy" // lalr1.cc:859
+ case 38:
+#line 417 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt::MetadataType > () = TokenPkt::IFACE;
}
-#line 1136 "parser.cc" // lalr1.cc:859
+#line 1163 "parser.cc" // lalr1.cc:859
break;
- case 37:
-#line 409 "parser.yy" // lalr1.cc:859
+ case 39:
+#line 421 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt::MetadataType > () = TokenPkt::SRC;
}
-#line 1144 "parser.cc" // lalr1.cc:859
+#line 1171 "parser.cc" // lalr1.cc:859
break;
- case 38:
-#line 413 "parser.yy" // lalr1.cc:859
+ case 40:
+#line 425 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt::MetadataType > () = TokenPkt::DST;
}
-#line 1152 "parser.cc" // lalr1.cc:859
+#line 1179 "parser.cc" // lalr1.cc:859
break;
- case 39:
-#line 417 "parser.yy" // lalr1.cc:859
+ case 41:
+#line 429 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt::MetadataType > () = TokenPkt::LEN;
}
-#line 1160 "parser.cc" // lalr1.cc:859
+#line 1187 "parser.cc" // lalr1.cc:859
break;
- case 40:
-#line 423 "parser.yy" // lalr1.cc:859
+ case 42:
+#line 435 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< uint32_t > () = ctx.convertUint32(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
-#line 1168 "parser.cc" // lalr1.cc:859
+#line 1195 "parser.cc" // lalr1.cc:859
break;
- case 41:
-#line 427 "parser.yy" // lalr1.cc:859
+ case 43:
+#line 439 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< uint32_t > () = 0;
}
-#line 1176 "parser.cc" // lalr1.cc:859
+#line 1203 "parser.cc" // lalr1.cc:859
break;
- case 42:
-#line 433 "parser.yy" // lalr1.cc:859
+ case 44:
+#line 445 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::CHADDR;
}
-#line 1184 "parser.cc" // lalr1.cc:859
+#line 1211 "parser.cc" // lalr1.cc:859
break;
- case 43:
-#line 437 "parser.yy" // lalr1.cc:859
+ case 45:
+#line 449 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::HLEN;
}
-#line 1192 "parser.cc" // lalr1.cc:859
+#line 1219 "parser.cc" // lalr1.cc:859
break;
- case 44:
-#line 441 "parser.yy" // lalr1.cc:859
+ case 46:
+#line 453 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::HTYPE;
}
-#line 1200 "parser.cc" // lalr1.cc:859
+#line 1227 "parser.cc" // lalr1.cc:859
break;
- case 45:
-#line 445 "parser.yy" // lalr1.cc:859
+ case 47:
+#line 457 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::CIADDR;
}
-#line 1208 "parser.cc" // lalr1.cc:859
+#line 1235 "parser.cc" // lalr1.cc:859
break;
- case 46:
-#line 449 "parser.yy" // lalr1.cc:859
+ case 48:
+#line 461 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::GIADDR;
}
-#line 1216 "parser.cc" // lalr1.cc:859
+#line 1243 "parser.cc" // lalr1.cc:859
break;
- case 47:
-#line 453 "parser.yy" // lalr1.cc:859
+ case 49:
+#line 465 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::YIADDR;
}
-#line 1224 "parser.cc" // lalr1.cc:859
+#line 1251 "parser.cc" // lalr1.cc:859
break;
- case 48:
-#line 457 "parser.yy" // lalr1.cc:859
+ case 50:
+#line 469 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::SIADDR;
}
-#line 1232 "parser.cc" // lalr1.cc:859
+#line 1259 "parser.cc" // lalr1.cc:859
break;
- case 49:
-#line 463 "parser.yy" // lalr1.cc:859
+ case 51:
+#line 473 "parser.yy" // lalr1.cc:859
+ {
+ yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::MSGTYPE;
+ }
+#line 1267 "parser.cc" // lalr1.cc:859
+ break;
+
+ case 52:
+#line 477 "parser.yy" // lalr1.cc:859
+ {
+ yylhs.value.as< TokenPkt4::FieldType > () = TokenPkt4::TRANSID;
+ }
+#line 1275 "parser.cc" // lalr1.cc:859
+ break;
+
+ case 53:
+#line 483 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt6::FieldType > () = TokenPkt6::MSGTYPE;
}
-#line 1240 "parser.cc" // lalr1.cc:859
+#line 1283 "parser.cc" // lalr1.cc:859
break;
- case 50:
-#line 467 "parser.yy" // lalr1.cc:859
+ case 54:
+#line 487 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenPkt6::FieldType > () = TokenPkt6::TRANSID;
}
-#line 1248 "parser.cc" // lalr1.cc:859
+#line 1291 "parser.cc" // lalr1.cc:859
break;
- case 51:
-#line 473 "parser.yy" // lalr1.cc:859
+ case 55:
+#line 493 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::PEERADDR;
}
-#line 1256 "parser.cc" // lalr1.cc:859
+#line 1299 "parser.cc" // lalr1.cc:859
break;
- case 52:
-#line 477 "parser.yy" // lalr1.cc:859
+ case 56:
+#line 497 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenRelay6Field::FieldType > () = TokenRelay6Field::LINKADDR;
}
-#line 1264 "parser.cc" // lalr1.cc:859
+#line 1307 "parser.cc" // lalr1.cc:859
break;
- case 53:
-#line 483 "parser.yy" // lalr1.cc:859
+ case 57:
+#line 503 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
-#line 1273 "parser.cc" // lalr1.cc:859
+#line 1316 "parser.cc" // lalr1.cc:859
break;
- case 54:
-#line 490 "parser.yy" // lalr1.cc:859
+ case 58:
+#line 510 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
-#line 1282 "parser.cc" // lalr1.cc:859
+#line 1325 "parser.cc" // lalr1.cc:859
break;
- case 55:
-#line 495 "parser.yy" // lalr1.cc:859
+ case 59:
+#line 515 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString("all"));
ctx.expression.push_back(str);
}
-#line 1291 "parser.cc" // lalr1.cc:859
+#line 1334 "parser.cc" // lalr1.cc:859
break;
-#line 1295 "parser.cc" // lalr1.cc:859
+#line 1338 "parser.cc" // lalr1.cc:859
default:
break;
}
@@ -1546,131 +1589,131 @@ namespace isc { namespace eval {
}
- const signed char EvalParser::yypact_ninf_ = -89;
+ const signed char EvalParser::yypact_ninf_ = -91;
const signed char EvalParser::yytable_ninf_ = -1;
const short int
EvalParser::yypact_[] =
{
- 4, 4, 4, 11, 18, 46, 69, 70, 93, 94,
- 85, -6, 36, -89, -89, -89, 105, 32, 100, 77,
- -89, 58, 58, 71, 67, 48, 62, 62, 26, -11,
- 72, -11, 76, -89, 4, 4, 62, -89, -89, -89,
- 106, 107, -89, 108, -89, -89, -89, -89, -89, -89,
- -89, -89, -89, -89, -89, -89, -89, 110, 111, 112,
- 98, 99, 92, 95, -89, -89, -89, -89, -89, 113,
- -89, 114, -89, -89, 125, -89, 116, 117, 118, 58,
- 58, 71, -11, -11, 89, 62, 120, 121, 1, 29,
- 14, 123, 124, 126, 127, 128, -89, 109, 136, -15,
- -3, -89, -89, -89, -89, -89, -89, 131, -89, -89,
- -89, 130, 132, 133, 134, 135, -29, -89, -89, 138,
- 139, -89, 58, 25, 25, 21, 104, 145, -89, -89,
- 151, 115, 58, 141, 143, 144, -89, 147, 148, 149,
- 58, 58, -89, 150, 51, 152, 153, 75, -89, -89,
- 154, 155, -89, -89, 25, 25
+ 3, 3, 3, -5, 13, 23, 4, 14, 54, 80,
+ 68, 37, 95, -91, -91, -91, -91, 93, 35, 98,
+ -91, 73, -91, 67, 67, 64, 50, 69, 45, 45,
+ 84, -20, 74, -20, 81, -91, 3, 3, 45, -91,
+ -91, -91, 109, 111, -91, 112, -91, -91, -91, -91,
+ -91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
+ -91, 114, 115, 116, 100, 103, 96, 97, -91, -91,
+ -91, -91, -91, 118, -91, 119, -91, -91, 129, -91,
+ 120, 121, 122, 67, 67, 64, -20, -20, 94, 45,
+ 123, 125, 16, 28, 6, 127, 128, 130, 131, 132,
+ -91, 113, 140, -14, -2, -91, -91, -91, -91, -91,
+ -91, 135, -91, -91, -91, 134, 136, 137, 138, 139,
+ -30, -91, -91, 142, 143, -91, 67, 58, 58, 12,
+ 108, 149, -91, -91, 155, 117, 67, 145, 147, 148,
+ -91, 150, 151, 152, 67, 67, -91, 153, 70, 156,
+ 157, 86, -91, -91, 154, 158, -91, -91, 58, 58
};
const unsigned char
EvalParser::yydefact_[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 14, 15, 16, 0, 2, 0, 0,
- 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0, 0, 0, 3, 31, 32,
- 0, 0, 35, 0, 36, 37, 38, 39, 20, 42,
- 43, 44, 45, 46, 47, 48, 21, 0, 0, 0,
- 0, 0, 0, 0, 49, 50, 22, 41, 40, 0,
- 27, 0, 26, 5, 6, 7, 0, 0, 0, 0,
+ 0, 0, 0, 14, 32, 15, 16, 0, 2, 0,
+ 31, 0, 4, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 3,
+ 33, 34, 0, 0, 37, 0, 38, 39, 40, 41,
+ 20, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 21, 0, 0, 0, 0, 0, 0, 0, 53, 54,
+ 22, 43, 42, 0, 27, 0, 26, 5, 6, 7,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 53, 0, 0, 0,
- 0, 33, 34, 8, 17, 9, 18, 0, 51, 52,
- 23, 0, 0, 0, 0, 0, 0, 25, 11, 29,
- 0, 12, 0, 0, 0, 0, 0, 0, 55, 54,
- 0, 0, 0, 0, 0, 0, 24, 0, 0, 0,
- 0, 0, 30, 0, 0, 0, 0, 0, 10, 19,
- 0, 0, 13, 28, 0, 0
+ 57, 0, 0, 0, 0, 35, 36, 8, 17, 9,
+ 18, 0, 55, 56, 23, 0, 0, 0, 0, 0,
+ 0, 25, 11, 29, 0, 12, 0, 0, 0, 0,
+ 0, 0, 59, 58, 0, 0, 0, 0, 0, 0,
+ 24, 0, 0, 0, 0, 0, 30, 0, 0, 0,
+ 0, 0, 10, 19, 0, 0, 13, 28, 0, 0
};
const signed char
EvalParser::yypgoto_[] =
{
- -89, -89, 20, -24, -22, -88, 78, -89, -20, -89,
- -89, -89, -89, -89
+ -91, -91, 30, -27, -91, -24, -90, 79, -91, -23,
+ -91, -91, -91, -91, -91
};
const short int
EvalParser::yydefgoto_[] =
{
- -1, 16, 17, 18, 40, 104, 43, 48, 69, 56,
- 66, 110, 97, 130
+ -1, 17, 18, 19, 20, 42, 108, 45, 50, 73,
+ 60, 70, 114, 101, 134
};
const unsigned char
EvalParser::yytable_[] =
{
- 41, 106, 62, 63, 118, 128, 120, 1, 29, 2,
- 30, 71, 75, 3, 4, 5, 121, 129, 101, 102,
- 103, 19, 20, 107, 6, 21, 108, 109, 119, 7,
- 134, 67, 22, 108, 109, 68, 106, 8, 34, 35,
- 9, 10, 101, 102, 11, 12, 101, 102, 105, 13,
- 31, 14, 32, 15, 73, 74, 149, 91, 92, 153,
- 23, 98, 94, 95, 64, 65, 149, 153, 101, 102,
- 148, 57, 58, 59, 49, 50, 51, 52, 53, 54,
- 55, 37, 6, 34, 35, 24, 25, 7, 44, 45,
- 46, 47, 101, 102, 152, 8, 26, 27, 9, 10,
- 133, 28, 60, 61, 38, 33, 39, 13, 36, 14,
- 138, 15, 82, 83, 30, 32, 70, 42, 145, 146,
- 72, 76, 77, 78, 79, 80, 81, 84, 86, 87,
- 85, 34, 88, 89, 90, 96, 99, 100, 111, 112,
- 117, 113, 114, 115, 116, 122, 123, 119, 124, 125,
- 126, 127, 131, 132, 135, 136, 139, 140, 141, 93,
- 0, 137, 142, 143, 0, 144, 147, 150, 151, 0,
- 154, 155
+ 43, 66, 67, 110, 132, 122, 1, 124, 2, 23,
+ 75, 79, 3, 4, 5, 111, 133, 125, 112, 113,
+ 26, 138, 71, 6, 112, 113, 72, 24, 7, 123,
+ 27, 21, 22, 105, 106, 107, 8, 25, 110, 9,
+ 10, 36, 37, 11, 12, 105, 106, 109, 13, 14,
+ 15, 31, 16, 32, 61, 62, 63, 28, 153, 95,
+ 96, 157, 102, 98, 99, 6, 77, 78, 153, 157,
+ 7, 46, 47, 48, 49, 105, 106, 39, 8, 36,
+ 37, 9, 10, 29, 30, 64, 65, 105, 106, 152,
+ 13, 14, 15, 35, 16, 51, 52, 53, 54, 55,
+ 56, 57, 137, 105, 106, 156, 38, 58, 59, 33,
+ 44, 34, 142, 40, 86, 41, 32, 87, 74, 34,
+ 149, 150, 68, 69, 80, 76, 81, 82, 83, 84,
+ 85, 88, 89, 90, 91, 36, 92, 93, 94, 103,
+ 100, 104, 115, 116, 121, 117, 118, 119, 120, 126,
+ 127, 123, 128, 129, 130, 131, 135, 136, 139, 140,
+ 143, 144, 145, 141, 97, 146, 147, 0, 148, 151,
+ 158, 154, 155, 0, 159
};
const short int
EvalParser::yycheck_[] =
{
- 22, 89, 26, 27, 19, 34, 9, 3, 14, 5,
- 16, 31, 36, 9, 10, 11, 19, 46, 17, 18,
- 19, 1, 2, 9, 20, 14, 12, 13, 43, 25,
- 9, 42, 14, 12, 13, 46, 124, 33, 6, 7,
- 36, 37, 17, 18, 40, 41, 17, 18, 19, 45,
- 14, 47, 16, 49, 34, 35, 144, 79, 80, 147,
- 14, 85, 82, 83, 38, 39, 154, 155, 17, 18,
- 19, 9, 10, 11, 26, 27, 28, 29, 30, 31,
- 32, 4, 20, 6, 7, 16, 16, 25, 21, 22,
- 23, 24, 17, 18, 19, 33, 3, 3, 36, 37,
- 122, 16, 40, 41, 46, 0, 48, 45, 8, 47,
- 132, 49, 14, 14, 16, 16, 44, 46, 140, 141,
- 44, 15, 15, 15, 14, 14, 14, 35, 15, 15,
- 35, 6, 16, 16, 16, 46, 16, 16, 15, 15,
- 4, 15, 15, 15, 35, 14, 16, 43, 16, 16,
- 16, 16, 14, 14, 9, 4, 15, 14, 14, 81,
- -1, 46, 15, 15, -1, 16, 16, 15, 15, -1,
- 16, 16
+ 24, 28, 29, 93, 34, 19, 3, 9, 5, 14,
+ 33, 38, 9, 10, 11, 9, 46, 19, 12, 13,
+ 16, 9, 42, 20, 12, 13, 46, 14, 25, 43,
+ 16, 1, 2, 17, 18, 19, 33, 14, 128, 36,
+ 37, 6, 7, 40, 41, 17, 18, 19, 45, 46,
+ 47, 14, 49, 16, 9, 10, 11, 3, 148, 83,
+ 84, 151, 89, 86, 87, 20, 36, 37, 158, 159,
+ 25, 21, 22, 23, 24, 17, 18, 4, 33, 6,
+ 7, 36, 37, 3, 16, 40, 41, 17, 18, 19,
+ 45, 46, 47, 0, 49, 26, 27, 28, 29, 30,
+ 31, 32, 126, 17, 18, 19, 8, 38, 39, 14,
+ 46, 16, 136, 46, 14, 48, 16, 14, 44, 16,
+ 144, 145, 38, 39, 15, 44, 15, 15, 14, 14,
+ 14, 35, 35, 15, 15, 6, 16, 16, 16, 16,
+ 46, 16, 15, 15, 4, 15, 15, 15, 35, 14,
+ 16, 43, 16, 16, 16, 16, 14, 14, 9, 4,
+ 15, 14, 14, 46, 85, 15, 15, -1, 16, 16,
+ 16, 15, 15, -1, 16
};
const unsigned char
EvalParser::yystos_[] =
{
0, 3, 5, 9, 10, 11, 20, 25, 33, 36,
- 37, 40, 41, 45, 47, 49, 51, 52, 53, 52,
- 52, 14, 14, 14, 16, 16, 3, 3, 16, 14,
- 16, 14, 16, 0, 6, 7, 8, 4, 46, 48,
- 54, 54, 46, 56, 21, 22, 23, 24, 57, 26,
- 27, 28, 29, 30, 31, 32, 59, 9, 10, 11,
- 40, 41, 53, 53, 38, 39, 60, 42, 46, 58,
- 44, 58, 44, 52, 52, 53, 15, 15, 15, 14,
- 14, 14, 14, 14, 35, 35, 15, 15, 16, 16,
- 16, 54, 54, 56, 58, 58, 46, 62, 53, 16,
- 16, 17, 18, 19, 55, 19, 55, 9, 12, 13,
- 61, 15, 15, 15, 15, 15, 35, 4, 19, 43,
- 9, 19, 14, 16, 16, 16, 16, 16, 34, 46,
- 63, 14, 14, 54, 9, 9, 4, 46, 54, 15,
- 14, 14, 15, 15, 16, 54, 54, 16, 19, 55,
- 15, 15, 19, 55, 16, 16
+ 37, 40, 41, 45, 46, 47, 49, 51, 52, 53,
+ 54, 52, 52, 14, 14, 14, 16, 16, 3, 3,
+ 16, 14, 16, 14, 16, 0, 6, 7, 8, 4,
+ 46, 48, 55, 55, 46, 57, 21, 22, 23, 24,
+ 58, 26, 27, 28, 29, 30, 31, 32, 38, 39,
+ 60, 9, 10, 11, 40, 41, 53, 53, 38, 39,
+ 61, 42, 46, 59, 44, 59, 44, 52, 52, 53,
+ 15, 15, 15, 14, 14, 14, 14, 14, 35, 35,
+ 15, 15, 16, 16, 16, 55, 55, 57, 59, 59,
+ 46, 63, 53, 16, 16, 17, 18, 19, 56, 19,
+ 56, 9, 12, 13, 62, 15, 15, 15, 15, 15,
+ 35, 4, 19, 43, 9, 19, 14, 16, 16, 16,
+ 16, 16, 34, 46, 64, 14, 14, 55, 9, 9,
+ 4, 46, 55, 15, 14, 14, 15, 15, 16, 55,
+ 55, 16, 19, 56, 15, 15, 19, 56, 16, 16
};
const unsigned char
@@ -1679,9 +1722,9 @@ namespace isc { namespace eval {
0, 50, 51, 52, 52, 52, 52, 52, 52, 52,
52, 52, 52, 52, 53, 53, 53, 53, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
- 53, 54, 54, 55, 55, 56, 57, 57, 57, 57,
- 58, 58, 59, 59, 59, 59, 59, 59, 59, 60,
- 60, 61, 61, 62, 63, 63
+ 53, 53, 54, 55, 55, 56, 56, 57, 58, 58,
+ 58, 58, 59, 59, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 61, 61, 62, 62, 63, 64, 64
};
const unsigned char
@@ -1692,7 +1735,7 @@ namespace isc { namespace eval {
3, 3, 3, 6, 8, 6, 3, 3, 11, 6,
9, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
@@ -1712,21 +1755,21 @@ namespace isc { namespace eval {
"\"vendor-class\"", "\"vendor\"", "\"*\"", "\"data\"", "\"enterprise\"",
"\"constant string\"", "\"integer\"", "\"constant hexstring\"",
"\"option name\"", "\"ip address\"", "$accept", "expression",
- "bool_expr", "string_expr", "option_code", "option_repr_type",
- "nest_level", "pkt_metadata", "enterprise_id", "pkt4_field",
- "pkt6_field", "relay6_field", "start_expr", "length_expr", YY_NULLPTR
+ "bool_expr", "string_expr", "integer_expr", "option_code",
+ "option_repr_type", "nest_level", "pkt_metadata", "enterprise_id",
+ "pkt4_field", "pkt6_field", "relay6_field", "start_expr", "length_expr", YY_NULLPTR
};
#if YYDEBUG
const unsigned short int
EvalParser::yyrline_[] =
{
- 0, 112, 112, 115, 116, 121, 126, 131, 136, 141,
- 161, 175, 184, 193, 205, 210, 215, 220, 225, 246,
- 261, 266, 280, 294, 309, 314, 319, 328, 338, 347,
- 360, 375, 379, 385, 389, 395, 404, 408, 412, 416,
- 422, 426, 432, 436, 440, 444, 448, 452, 456, 462,
- 466, 472, 476, 482, 489, 494
+ 0, 113, 113, 116, 117, 122, 127, 132, 137, 142,
+ 162, 176, 185, 194, 206, 211, 216, 221, 226, 247,
+ 262, 267, 281, 295, 310, 315, 320, 329, 339, 348,
+ 361, 374, 381, 387, 391, 397, 401, 407, 416, 420,
+ 424, 428, 434, 438, 444, 448, 452, 456, 460, 464,
+ 468, 472, 476, 482, 486, 492, 496, 502, 509, 514
};
// Print the state stack on the debug stream.
@@ -1761,8 +1804,8 @@ namespace isc { namespace eval {
#line 13 "parser.yy" // lalr1.cc:1167
} } // isc::eval
-#line 1765 "parser.cc" // lalr1.cc:1167
-#line 501 "parser.yy" // lalr1.cc:1168
+#line 1808 "parser.cc" // lalr1.cc:1167
+#line 521 "parser.yy" // lalr1.cc:1168
void
isc::eval::EvalParser::error(const location_type& loc,
diff --git a/src/lib/eval/parser.h b/src/lib/eval/parser.h
index f61db8fff2..be22231f99 100644
--- a/src/lib/eval/parser.h
+++ b/src/lib/eval/parser.h
@@ -320,6 +320,7 @@ namespace isc { namespace eval {
// option_code
char dummy7[sizeof(uint16_t)];
+ // integer_expr
// enterprise_id
char dummy8[sizeof(uint32_t)];
@@ -914,9 +915,9 @@ namespace isc { namespace eval {
enum
{
yyeof_ = 0,
- yylast_ = 171, ///< Last index in yytable_.
- yynnts_ = 14, ///< Number of nonterminal symbols.
- yyfinal_ = 33, ///< Termination state number.
+ yylast_ = 174, ///< Last index in yytable_.
+ yynnts_ = 15, ///< Number of nonterminal symbols.
+ yyfinal_ = 35, ///< Termination state number.
yyterror_ = 1,
yyerrcode_ = 256,
yyntokens_ = 50 ///< Number of tokens.
@@ -1001,23 +1002,23 @@ namespace isc { namespace eval {
{
switch (other.type_get ())
{
- case 55: // option_repr_type
+ case 56: // option_repr_type
value.copy< TokenOption::RepresentationType > (other.value);
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
value.copy< TokenPkt4::FieldType > (other.value);
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
value.copy< TokenPkt6::FieldType > (other.value);
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
value.copy< TokenPkt::MetadataType > (other.value);
break;
- case 61: // relay6_field
+ case 62: // relay6_field
value.copy< TokenRelay6Field::FieldType > (other.value);
break;
@@ -1029,15 +1030,16 @@ namespace isc { namespace eval {
value.copy< std::string > (other.value);
break;
- case 54: // option_code
+ case 55: // option_code
value.copy< uint16_t > (other.value);
break;
- case 58: // enterprise_id
+ case 54: // integer_expr
+ case 59: // enterprise_id
value.copy< uint32_t > (other.value);
break;
- case 56: // nest_level
+ case 57: // nest_level
value.copy< uint8_t > (other.value);
break;
@@ -1058,23 +1060,23 @@ namespace isc { namespace eval {
(void) v;
switch (this->type_get ())
{
- case 55: // option_repr_type
+ case 56: // option_repr_type
value.copy< TokenOption::RepresentationType > (v);
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
value.copy< TokenPkt4::FieldType > (v);
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
value.copy< TokenPkt6::FieldType > (v);
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
value.copy< TokenPkt::MetadataType > (v);
break;
- case 61: // relay6_field
+ case 62: // relay6_field
value.copy< TokenRelay6Field::FieldType > (v);
break;
@@ -1086,15 +1088,16 @@ namespace isc { namespace eval {
value.copy< std::string > (v);
break;
- case 54: // option_code
+ case 55: // option_code
value.copy< uint16_t > (v);
break;
- case 58: // enterprise_id
+ case 54: // integer_expr
+ case 59: // enterprise_id
value.copy< uint32_t > (v);
break;
- case 56: // nest_level
+ case 57: // nest_level
value.copy< uint8_t > (v);
break;
@@ -1202,23 +1205,23 @@ namespace isc { namespace eval {
// Type destructor.
switch (yytype)
{
- case 55: // option_repr_type
+ case 56: // option_repr_type
value.template destroy< TokenOption::RepresentationType > ();
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
value.template destroy< TokenPkt4::FieldType > ();
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
value.template destroy< TokenPkt6::FieldType > ();
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
value.template destroy< TokenPkt::MetadataType > ();
break;
- case 61: // relay6_field
+ case 62: // relay6_field
value.template destroy< TokenRelay6Field::FieldType > ();
break;
@@ -1230,15 +1233,16 @@ namespace isc { namespace eval {
value.template destroy< std::string > ();
break;
- case 54: // option_code
+ case 55: // option_code
value.template destroy< uint16_t > ();
break;
- case 58: // enterprise_id
+ case 54: // integer_expr
+ case 59: // enterprise_id
value.template destroy< uint32_t > ();
break;
- case 56: // nest_level
+ case 57: // nest_level
value.template destroy< uint8_t > ();
break;
@@ -1265,23 +1269,23 @@ namespace isc { namespace eval {
super_type::move(s);
switch (this->type_get ())
{
- case 55: // option_repr_type
+ case 56: // option_repr_type
value.move< TokenOption::RepresentationType > (s.value);
break;
- case 59: // pkt4_field
+ case 60: // pkt4_field
value.move< TokenPkt4::FieldType > (s.value);
break;
- case 60: // pkt6_field
+ case 61: // pkt6_field
value.move< TokenPkt6::FieldType > (s.value);
break;
- case 57: // pkt_metadata
+ case 58: // pkt_metadata
value.move< TokenPkt::MetadataType > (s.value);
break;
- case 61: // relay6_field
+ case 62: // relay6_field
value.move< TokenRelay6Field::FieldType > (s.value);
break;
@@ -1293,15 +1297,16 @@ namespace isc { namespace eval {
value.move< std::string > (s.value);
break;
- case 54: // option_code
+ case 55: // option_code
value.move< uint16_t > (s.value);
break;
- case 58: // enterprise_id
+ case 54: // integer_expr
+ case 59: // enterprise_id
value.move< uint32_t > (s.value);
break;
- case 56: // nest_level
+ case 57: // nest_level
value.move< uint8_t > (s.value);
break;
@@ -1660,7 +1665,7 @@ namespace isc { namespace eval {
#line 13 "parser.yy" // lalr1.cc:377
} } // isc::eval
-#line 1664 "parser.h" // lalr1.cc:377
+#line 1669 "parser.h" // lalr1.cc:377
diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy
index 62a6bd270c..5bfadcfed0 100644
--- a/src/lib/eval/parser.yy
+++ b/src/lib/eval/parser.yy
@@ -89,6 +89,7 @@ using namespace isc::eval;
%type <uint16_t> option_code
%type <uint32_t> enterprise_id
+%type <uint32_t> integer_expr
%type <TokenOption::RepresentationType> option_repr_type
%type <TokenRelay6Field::FieldType> relay6_field
%type <uint8_t> nest_level
@@ -370,8 +371,19 @@ string_expr : STRING
TokenVendor::DATA, index));
ctx.expression.push_back(vendor_class);
}
+ | integer_expr
+ {
+ TokenPtr integer(new TokenInteger($1));
+ ctx.expression.push_back(integer);
+ }
;
+integer_expr : INTEGER
+ {
+ $$ = ctx.convertUint32($1, @1);
+ }
+ ;
+
option_code : INTEGER
{
$$ = ctx.convertOptionCode($1, @1);
@@ -457,6 +469,14 @@ pkt4_field : CHADDR
{
$$ = TokenPkt4::SIADDR;
}
+ | MSGTYPE
+ {
+ $$ = TokenPkt4::MSGTYPE;
+ }
+ | TRANSID
+ {
+ $$ = TokenPkt4::TRANSID;
+ }
;
pkt6_field : MSGTYPE
diff --git a/src/lib/eval/tests/context_unittest.cc b/src/lib/eval/tests/context_unittest.cc
index d8811d1ffe..25b02b47f7 100644
--- a/src/lib/eval/tests/context_unittest.cc
+++ b/src/lib/eval/tests/context_unittest.cc
@@ -96,6 +96,18 @@ public:
EXPECT_TRUE(eq);
}
+ /// @brief Checks if the given token is integer with expected value
+ ///
+ /// @param token token to be inspected
+ /// @param exp_value expected integer value of the token
+ void checkTokenInteger(const TokenPtr& token, uint32_t exp_value) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenInteger> integer =
+ boost::dynamic_pointer_cast<TokenInteger>(token);
+ ASSERT_TRUE(integer);
+ EXPECT_EQ(exp_value, integer->getInteger());
+ }
+
/// @brief checks if the given token is an option with the expected code
/// and representation type
/// @param token token to be checked
@@ -929,7 +941,7 @@ TEST_F(EvalContextTest, relay6OptionLimits) {
checkError("relay6[32].option[123].text == 'foo'",
"<string>:1.8-9: Nest level has invalid value in 32. "
"Allowed range: 0..31");
-
+
// next level must be a positive number
checkError("relay6[-1].option[123].text == 'foo'",
"<string>:1.8-9: Invalid value in -1. Allowed range: 0..255");
@@ -1003,12 +1015,12 @@ TEST_F(EvalContextTest, pkt4FieldSiaddr) {
// Tests whether message type field in DHCPv6 can be accessed.
TEST_F(EvalContextTest, pkt6FieldMsgtype) {
- testPkt6Field("pkt6.msgtype == '1'", TokenPkt6::MSGTYPE, 3);
+ testPkt6Field("pkt6.msgtype == 1", TokenPkt6::MSGTYPE, 3);
}
// Tests whether transaction id field in DHCPv6 can be accessed.
TEST_F(EvalContextTest, pkt6FieldTransid) {
- testPkt6Field("pkt6.transid == '1'", TokenPkt6::TRANSID, 3);
+ testPkt6Field("pkt6.transid == 1", TokenPkt6::TRANSID, 3);
}
// Tests if the linkaddr field in a Relay6 encapsulation can be accessed.
@@ -1200,10 +1212,17 @@ TEST_F(EvalContextTest, scanErrors) {
TEST_F(EvalContextTest, scanParseErrors) {
checkError("", "<string>:1.1: syntax error, unexpected end of file");
checkError(" ", "<string>:1.2: syntax error, unexpected end of file");
- checkError("0x", "<string>:1.1: syntax error, unexpected integer");
+ checkError("0x", "<string>:1.2: Invalid character: x");
checkError("0abc",
- "<string>:1.1: syntax error, unexpected integer");
- checkError("10.0.1", "<string>:1.1-2: syntax error, unexpected integer");
+ "<string>:1.2: Invalid character: a");
+
+ // This one is a little bid odd. This is a truncated address, so it's not
+ // recognized as an address. Instead, the first token (10) is recognized as
+ // an integer. The only thing we can do with integers right now is test
+ // for equality, so the only possible next token is ==. There's a dot
+ // instead, so an error is reported.
+ checkError("10.0.1", "<string>:1.3: syntax error, unexpected ., expecting ==");
+
checkError("10.256.0.1",
"<string>:1.1-10: Failed to convert 10.256.0.1 to "
"an IP address.");
@@ -1332,10 +1351,13 @@ TEST_F(EvalContextTest, typeErrors) {
checkError("substring('foobar',0x32,1) == 'foo'",
"<string>:1.20-23: syntax error, unexpected constant "
"hexstring, expecting integer");
- checkError("concat('foo',3) == 'foo3'",
- "<string>:1.14: syntax error, unexpected integer");
- checkError("concat(3,'foo') == '3foo'",
- "<string>:1.8: syntax error, unexpected integer");
+
+ // With the #4483 addition, all integers are treated as 4 byte strings,
+ // so those checks no longer makes sense. Commeting it out.
+ // checkError("concat('foo',3) == 'foo3'",
+ // "<string>:1.14: syntax error, unexpected integer");
+ // checkError("concat(3,'foo') == '3foo'",
+ // "<string>:1.8: syntax error, unexpected integer");
checkError("('foo' == 'bar') == 'false'",
"<string>:1.18-19: syntax error, unexpected ==, "
"expecting end of file");
@@ -1439,4 +1461,23 @@ TEST_F(EvalContextTest, vendorClass6DataIndex) {
testVendorClass("vendor-class[4491].data[3] == 0x1234", Option::V6, 4491, 3);
}
+// Checks if integer expressions can be parsed and checked for equality.
+TEST_F(EvalContextTest, integer1) {
+
+ EvalContext eval(Option::V6);
+
+ EXPECT_NO_THROW(parsed_ = eval.parseString("1 == 2"));
+ EXPECT_TRUE(parsed_);
+
+ ASSERT_EQ(3, eval.expression.size());
+
+ TokenPtr tmp = eval.expression.at(0);
+ ASSERT_TRUE(tmp);
+ checkTokenInteger(tmp, 1);
+ tmp = eval.expression.at(1);
+
+ ASSERT_TRUE(tmp);
+ checkTokenInteger(tmp, 2);
+}
+
};
diff --git a/src/lib/eval/tests/evaluate_unittest.cc b/src/lib/eval/tests/evaluate_unittest.cc
index ce5ed05c40..64950c9704 100644
--- a/src/lib/eval/tests/evaluate_unittest.cc
+++ b/src/lib/eval/tests/evaluate_unittest.cc
@@ -6,6 +6,7 @@
#include <config.h>
#include <eval/evaluate.h>
+#include <eval/eval_context.h>
#include <eval/token.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
@@ -282,4 +283,161 @@ TEST_F(EvaluateTest, complex) {
EXPECT_TRUE(result_);
}
+/// @brief Generic class for parsing expressions and evaluating them.
+///
+/// The main purpose of this class is to provide a generic interface to the
+/// eval library, so everything (expression parsing and then evaluation for
+/// given packets) can be done in one simple call.
+///
+/// These tests may be somewhat redundant to other more specialized tests, but
+/// the idea here is to mass produce tests that are trivial to write.
+class ExpressionsTest : public EvaluateTest {
+public:
+
+ /// @brief Checks if expression can be parsed and evaluated
+ ///
+ /// There are skeleton packets created in pkt4_ and pkt6_. Make sure you
+ /// tweak them as needed before calling this method.
+ ///
+ /// @param u universe (V4 or V6)
+ /// @param expr expression to be parsed
+ /// @param exp_result expected result (true or false)
+ void testExpression(const Option::Universe& u, const std::string& expr,
+ const bool exp_result) {
+
+ EvalContext eval(u);
+ bool result = false;
+ bool parsed = false;
+
+ EXPECT_NO_THROW(parsed = eval.parseString(expr))
+ << " while parsing expression " << expr;
+ EXPECT_TRUE(parsed) << " for expression " << expr;
+
+ switch (u) {
+ case Option::V4:
+ ASSERT_NO_THROW(result = evaluate(eval.expression, *pkt4_))
+ << " for expression " << expr;
+ break;
+ case Option::V6:
+ ASSERT_NO_THROW(result = evaluate(eval.expression, *pkt6_))
+ << " for expression " << expr;
+ break;
+ }
+
+ EXPECT_EQ(exp_result, result) << " for expression " << expr;
+ }
+
+ /// @brief Checks that specified expression throws expected exception.
+ ///
+ /// @tparam ex exception type expected to be thrown
+ /// @param expr expression to be evaluated
+ template<typename ex>
+ void testExpressionNegative(const std::string& expr,
+ const Option::Universe& u = Option::V4) {
+ EvalContext eval(u);
+
+ EXPECT_THROW(eval.parseString(expr), ex) << "while parsing expression "
+ << expr;
+ }
+};
+
+// This is a quick way to check if certain expressions are valid or not and
+// whether the whole expression makes sense. This particular test checks if
+// integers can be used properly in expressions. There are many places where
+// integers are used. This particular test checks if pkt6.msgtype returns
+// something that can be compared with integers.
+//
+// For basic things we can take advantage of the skeleton packets created in
+// EvaluateTest constructors: The packet type is DISCOVER in DHCPv4 and
+// SOLICIT in DHCPv6. There is one option added with code 100 and content
+// being either "hundred4" or "hundred6" depending on the universe.
+
+// Tests if pkt6.msgtype returns something that can be compared with integers.
+TEST_F(ExpressionsTest, expressionsInteger1) {
+ testExpression(Option::V6, "pkt6.msgtype == 1", true);
+ testExpression(Option::V6, "pkt6.msgtype == 2", false);
+
+ testExpression(Option::V6, "pkt6.msgtype == 0x00000001", true);
+ testExpression(Option::V6, "pkt6.msgtype == 0x00000002", false);
+}
+
+// Tests if pkt6.transid returns something that can be compared with integers.
+TEST_F(ExpressionsTest, expressionsInteger2) {
+ testExpression(Option::V6, "pkt6.transid == 0", false);
+ testExpression(Option::V6, "pkt6.transid == 12345", true);
+ testExpression(Option::V6, "pkt6.transid == 12346", false);
+}
+
+// Tests if pkt4.transid returns something that can be compared with integers.
+TEST_F(ExpressionsTest, expressionsInteger3) {
+ testExpression(Option::V4, "pkt4.transid == 0", false);
+ testExpression(Option::V4, "pkt4.transid == 12345", true);
+ testExpression(Option::V4, "pkt4.transid == 12346", false);
+}
+
+// Tests if integers can be compared with integers.
+TEST_F(ExpressionsTest, expressionsInteger4) {
+ testExpression(Option::V6, "0 == 0", true);
+ testExpression(Option::V6, "2 == 3", false);
+}
+
+// Tests if pkt4.hlen and pkt4.htype return values that can be compared with integers.
+TEST_F(ExpressionsTest, expressionsPkt4Hlen) {
+
+ // By default there's no hardware set up. The default Pkt4 constructor
+ // creates HWAddr(), which has hlen=0 and htype set to HTYPE_ETHER.
+ testExpression(Option::V4, "pkt4.hlen == 0", true);
+ testExpression(Option::V4, "pkt4.htype == 1", true);
+
+ // Ok, let's initialize the hardware address to something plausible.
+ const size_t hwaddr_len = 6;
+ const uint16_t expected_htype = 123;
+ std::vector<uint8_t> hw(hwaddr_len,0);
+ for (int i = 0; i < hwaddr_len; i++) {
+ hw[i] = i + 1;
+ }
+ pkt4_->setHWAddr(expected_htype, hwaddr_len, hw);
+
+ testExpression(Option::V4, "pkt4.hlen == 0", false);
+ testExpression(Option::V4, "pkt4.hlen == 5", false);
+ testExpression(Option::V4, "pkt4.hlen == 6", true);
+ testExpression(Option::V4, "pkt4.hlen == 7", false);
+
+ testExpression(Option::V4, "pkt4.htype == 0", false);
+ testExpression(Option::V4, "pkt4.htype == 122", false);
+ testExpression(Option::V4, "pkt4.htype == 123", true);
+ testExpression(Option::V4, "pkt4.htype == 124", false);
+
+ testExpression(Option::V4, "pkt4.mac == 0x010203040506", true);
+}
+
+// Test if expressions message type can be detected in Pkt4.
+// It also doubles as a check for integer comparison here.
+TEST_F(ExpressionsTest, expressionsPkt4type) {
+
+ // We can inspect the option content directly, but
+ // it requires knowledge of the option type and its format.
+ testExpression(Option::V4, "option[53].hex == 0x0", false);
+ testExpression(Option::V4, "option[53].hex == 0x1", true);
+ testExpression(Option::V4, "option[53].hex == 0x2", false);
+
+ // It's easier to simply use the pkt4.msgtype
+ testExpression(Option::V4, "pkt4.msgtype == 0", false);
+ testExpression(Option::V4, "pkt4.msgtype == 1", true);
+ testExpression(Option::V4, "pkt4.msgtype == 2", false);
+}
+
+// This tests if inappropriate values (negative, too large) are
+// rejected, but extreme values still allowed for uint32_t are ok.
+TEST_F(ExpressionsTest, invalidIntegers) {
+
+ // These are the extreme uint32_t values that still should be accepted.
+ testExpression(Option::V4, "4294967295 == 0", false);
+
+ // Negative integers should be rejected.
+ testExpressionNegative<EvalParseError>("4294967295 == -1");
+
+ // Oops, one too much.
+ testExpressionNegative<EvalParseError>("4294967296 == 0");
+}
};
diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc
index 96b52137e1..2fcf1c549d 100644
--- a/src/lib/eval/tests/token_unittest.cc
+++ b/src/lib/eval/tests/token_unittest.cc
@@ -7,6 +7,7 @@
#include <config.h>
#include <fstream>
#include <eval/token.h>
+#include <eval/eval_context.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcp/dhcp4.h>
@@ -183,25 +184,22 @@ public:
}
/// @brief Convenience function. Removes token and values stacks.
- void clearStack() {
+ /// @param token specifies if the convenience token should be removed or not
+ void clearStack(bool token = true) {
while (!values_.empty()) {
values_.pop();
}
-
- t_.reset();
+ if (token) {
+ t_.reset();
+ }
}
- /// @brief Aux. function that stores integer values as 4 bytes string.
+ /// @brief Aux. function that stores integer values as 4 byte string.
///
/// @param value integer value to be stored
- /// @return 4 bytes long string with encoded value.
+ /// @return 4 byte long string with encoded value.
string encode(uint32_t value) {
- string tmp(4,0);
- tmp[0] = value >> 24;
- tmp[1] = value >> 16;
- tmp[2] = value >> 8;
- tmp[3] = value;
- return (tmp);
+ return EvalContext::fromUint32(value);
}
TokenPtr t_; ///< Just a convenience pointer
@@ -484,6 +482,26 @@ public:
evaluate(u, expected);
}
+
+ /// @brief Tests if TokenInteger evaluates to the proper value
+ ///
+ /// @param expected expected string representation on stack after evaluation
+ /// @param value integer value passed to constructor
+ void testInteger(const std::string& expected, uint32_t value) {
+
+ clearStack();
+
+ ASSERT_NO_THROW(t_.reset(new TokenInteger(value)));
+
+ // The universe (v4 or v6) shouldn't have any impact on this,
+ // but let's check it anyway.
+ evaluate(Option::V4, expected);
+
+ clearStack(false);
+ evaluate(Option::V6, expected);
+
+ clearStack(true);
+ }
};
// This tests the toBool() conversions
@@ -1329,6 +1347,24 @@ TEST_F(TokenTest, pkt4Fields) {
ASSERT_EQ(4, values_.top().size());
EXPECT_EQ(0, memcmp(expected_addr, &values_.top()[0], 4));
+ // Check msgtype.
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::MSGTYPE)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ ASSERT_EQ(4, values_.top().size());
+ string exp_msgtype = encode(DHCPDISCOVER);
+ EXPECT_EQ(0, memcmp(&exp_msgtype[0], &values_.top()[0], 4));
+
+ // Check transaction-id
+ clearStack();
+ ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::TRANSID)));
+ EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
+ ASSERT_EQ(1, values_.size());
+ ASSERT_EQ(4, values_.top().size());
+ string exp_transid = encode(12345);
+ EXPECT_EQ(0, memcmp(&exp_transid[0], &values_.top()[0], 4));
+
// Check a DHCPv6 packet throws.
clearStack();
ASSERT_NO_THROW(t_.reset(new TokenPkt4(TokenPkt4::HLEN)));
@@ -1349,6 +1385,8 @@ TEST_F(TokenTest, pkt4Fields) {
addString("EVAL_DEBUG_PKT4 Pushing PKT4 field ciaddr with value 0xC0000202");
addString("EVAL_DEBUG_PKT4 Pushing PKT4 field yiaddr with value 0xC0000203");
addString("EVAL_DEBUG_PKT4 Pushing PKT4 field siaddr with value 0xC0000204");
+ addString("EVAL_DEBUG_PKT4 Pushing PKT4 field msgtype with value 0x00000001");
+ addString("EVAL_DEBUG_PKT4 Pushing PKT4 field transid with value 0x00003039");
EXPECT_TRUE(checkFile());
}
@@ -2721,4 +2759,14 @@ TEST_F(TokenTest, vendorClass6DataIndex) {
EXPECT_TRUE(checkFile());
}
+// Checks if various values can be represented as integer tokens
+TEST_F(TokenTest, integer) {
+ testInteger(encode(0), 0);
+ testInteger(encode(6), 6);
+ testInteger(encode(255), 255);
+ testInteger(encode(256), 256);
+ testInteger(encode(1410), 1410);
+ testInteger(encode(4294967295), 4294967295);
+}
+
};
diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc
index 7ef16a0348..a6471929a2 100644
--- a/src/lib/eval/token.cc
+++ b/src/lib/eval/token.cc
@@ -6,6 +6,7 @@
#include <eval/token.h>
#include <eval/eval_log.h>
+#include <eval/eval_context.h>
#include <util/encode/hex.h>
#include <util/io_utilities.h>
#include <asiolink/io_address.h>
@@ -200,11 +201,12 @@ TokenPkt::evaluate(Pkt& pkt, ValueStack& values) {
string value;
vector<uint8_t> binary;
string type_str;
- uint32_t len;
bool is_binary = true;
+ bool print_hex = true;
switch (type_) {
case IFACE:
is_binary = false;
+ print_hex = false;
value = pkt.getIface();
type_str = "iface";
break;
@@ -221,9 +223,8 @@ TokenPkt::evaluate(Pkt& pkt, ValueStack& values) {
// (with UDP transport it fits in 16 bits)
// the len() method is not const because of DHCPv6 relays.
// We assume here it has no bad side effects...
- len = static_cast<uint32_t>(const_cast<Pkt&>(pkt).len());
- binary.resize(sizeof(uint32_t));
- static_cast<void>(writeUint32(len, &binary[0], binary.size()));
+ value = EvalContext::fromUint32(static_cast<uint32_t>(const_cast<Pkt&>(pkt).len()));
+ is_binary = false;
type_str = "len";
break;
@@ -243,13 +244,14 @@ TokenPkt::evaluate(Pkt& pkt, ValueStack& values) {
// Log what we pushed
LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT)
.arg(type_str)
- .arg(is_binary ? toHex(value) : value);
+ .arg(print_hex ? toHex(value) : value);
}
void
TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) {
vector<uint8_t> binary;
+ string value;
string type_str;
try {
// Check if it's a Pkt4. If it's not, the dynamic_cast will throw
@@ -292,21 +294,23 @@ TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) {
case HLEN:
// Pad the uint8_t field to 4 bytes.
- binary.push_back(0);
- binary.push_back(0);
- binary.push_back(0);
- binary.push_back(pkt4.getHlen());
+ value = EvalContext::fromUint32(pkt4.getHlen());
type_str = "hlen";
break;
case HTYPE:
// Pad the uint8_t field to 4 bytes.
- binary.push_back(0);
- binary.push_back(0);
- binary.push_back(0);
- binary.push_back(pkt4.getHtype());
+ value = EvalContext::fromUint32(pkt4.getHtype());
type_str = "htype";
break;
+ case MSGTYPE:
+ value = EvalContext::fromUint32(pkt4.getType());
+ type_str = "msgtype";
+ break;
+ case TRANSID:
+ value = EvalContext::fromUint32(pkt4.getTransid());
+ type_str = "transid";
+ break;
default:
isc_throw(EvalTypeError, "Bad field specified: "
@@ -317,9 +321,8 @@ TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) {
isc_throw(EvalTypeError, "Specified packet is not a Pkt4");
}
- string value;
- value.resize(binary.size());
if (!binary.empty()) {
+ value.resize(binary.size());
memmove(&value[0], &binary[0], binary.size());
}
values.push(value);
@@ -333,7 +336,7 @@ TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) {
void
TokenPkt6::evaluate(Pkt& pkt, ValueStack& values) {
- vector<uint8_t> binary;
+ string value;
string type_str;
try {
// Check if it's a Pkt6. If it's not the dynamic_cast will throw
@@ -344,20 +347,13 @@ TokenPkt6::evaluate(Pkt& pkt, ValueStack& values) {
switch (type_) {
case MSGTYPE: {
// msg type is an uint8_t integer. We want a 4 byte string so 0 pad.
- binary.push_back(0);
- binary.push_back(0);
- binary.push_back(0);
- binary.push_back(pkt6.getType());
+ value = EvalContext::fromUint32(pkt6.getType());
type_str = "msgtype";
break;
}
case TRANSID: {
// transaction id is an uint32_t integer. We want a 4 byte string so copy
- uint32_t transid = pkt6.getTransid();
- binary.push_back(transid >> 24);
- binary.push_back((transid >> 16) & 0xFF);
- binary.push_back((transid >> 8) & 0xFF);
- binary.push_back(transid & 0xFF);
+ value = EvalContext::fromUint32(pkt6.getTransid());
type_str = "transid";
break;
}
@@ -370,9 +366,6 @@ TokenPkt6::evaluate(Pkt& pkt, ValueStack& values) {
isc_throw(EvalTypeError, "Specified packet is not Pkt6");
}
- string value;
- value.resize(binary.size());
- memmove(&value[0], &binary[0], binary.size());
values.push(value);
// Log what we pushed
@@ -879,3 +872,8 @@ void TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) {
isc_throw(EvalTypeError, "Invalid field specified." << field_);
}
}
+
+TokenInteger::TokenInteger(const uint32_t value)
+ :TokenString(EvalContext::fromUint32(value)), int_value_(value) {
+
+}
diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h
index aabca84397..37af93c468 100644
--- a/src/lib/eval/token.h
+++ b/src/lib/eval/token.h
@@ -155,6 +155,35 @@ protected:
std::string value_; ///< Constant value
};
+/// @brief Token representing an unsigned 32 bit integer
+///
+/// For performance reasons, the constant integer value is converted to a string
+/// just once (in the constructor). Afterwards, this effectively works as a constant
+/// 4 byte long string. Hence this class is derived from TokenString and
+/// does not even need its own evaluate() method.
+class TokenInteger : public TokenString {
+public:
+ /// @brief Integer value set during construction.
+ ///
+ /// The value is converted to string and stored in value_ provided by the
+ /// base class.
+ ///
+ /// @param value integer value to be stored.
+ TokenInteger(const uint32_t value);
+
+ /// @brief Returns integer value
+ ///
+ /// Used in tests only.
+ ///
+ /// @return integer value
+ uint32_t getInteger() const {
+ return (int_value_);
+ }
+
+protected:
+ uint32_t int_value_; ///< value as integer (stored for testing only)
+};
+
/// @brief Token representing an IP address as a constant string
///
/// This token holds the value of an IP address as a constant string,
@@ -413,7 +442,9 @@ public:
YIADDR, ///< yiaddr (IPv4 address)
SIADDR, ///< siaddr (IPv4 address)
HLEN, ///< hlen (hardware address length)
- HTYPE ///< htype (hardware address type)
+ HTYPE, ///< htype (hardware address type)
+ MSGTYPE, ///< message type (not really a field, content of option 53)
+ TRANSID, ///< transaction-id (xid)
};
/// @brief Constructor (does nothing)
diff --git a/src/lib/log/logger_level_impl.cc b/src/lib/log/logger_level_impl.cc
index d5cff064d0..ae8a3ed568 100644
--- a/src/lib/log/logger_level_impl.cc
+++ b/src/lib/log/logger_level_impl.cc
@@ -179,20 +179,18 @@ LoggerLevelImpl::logLevelFromString(const log4cplus::tstring& level) {
// return the string DEBUG, else return the empty string.
LoggerLevelImpl::LogLevelString
LoggerLevelImpl::logLevelToString(log4cplus::LogLevel level) {
- static const tstring debug_string("DEBUG");
- static const tstring empty_string;
Level bindlevel = convertToBindLevel(level);
Severity& severity = bindlevel.severity;
int& dbglevel = bindlevel.dbglevel;
if ((severity == DEBUG) &&
((dbglevel >= MIN_DEBUG_LEVEL) && (dbglevel <= MAX_DEBUG_LEVEL))) {
- return (debug_string);
+ return (tstring("DEBUG"));
}
// Unknown, so return empty string for log4cplus to try other conversion
// functions.
- return (empty_string);
+ return (tstring());
}
// Initialization. Register the conversion functions with the LogLevelManager.
diff --git a/src/lib/log/logger_level_impl.h b/src/lib/log/logger_level_impl.h
index 9d4dd4b0aa..20321bd328 100644
--- a/src/lib/log/logger_level_impl.h
+++ b/src/lib/log/logger_level_impl.h
@@ -58,11 +58,7 @@ namespace log {
class LoggerLevelImpl {
public:
-#if (LOG4CPLUS_VERSION >= LOG4CPLUS_MAKE_VERSION(1, 1, 0))
- typedef log4cplus::tstring const & LogLevelString;
-#else
- typedef log4cplus::tstring LogLevelString;
-#endif
+typedef log4cplus::tstring LogLevelString;
/// \brief Convert Kea level to log4cplus logging level
///
diff --git a/src/lib/log/message_dictionary.cc b/src/lib/log/message_dictionary.cc
index f9c5ee3b20..3d670e256a 100644
--- a/src/lib/log/message_dictionary.cc
+++ b/src/lib/log/message_dictionary.cc
@@ -13,6 +13,11 @@ using namespace std;
namespace isc {
namespace log {
+// Constructor
+
+MessageDictionary::MessageDictionary() : dictionary_(), empty_("") {
+}
+
// (Virtual) Destructor
MessageDictionary::~MessageDictionary() {
@@ -91,10 +96,9 @@ MessageDictionary::load(const char* messages[]) {
const string&
MessageDictionary::getText(const std::string& ident) const {
- static const string empty("");
Dictionary::const_iterator i = dictionary_.find(ident);
if (i == dictionary_.end()) {
- return (empty);
+ return (empty_);
}
else {
return (i->second);
diff --git a/src/lib/log/message_dictionary.h b/src/lib/log/message_dictionary.h
index ac8541a863..7863dc6a3e 100644
--- a/src/lib/log/message_dictionary.h
+++ b/src/lib/log/message_dictionary.h
@@ -50,7 +50,8 @@ public:
typedef std::map<std::string, std::string> Dictionary;
typedef Dictionary::const_iterator const_iterator;
- // Default constructor and assignment operator are OK for this class
+ /// \brief Constructor
+ MessageDictionary();
/// \brief Virtual Destructor
virtual ~MessageDictionary();
@@ -198,6 +199,7 @@ public:
private:
Dictionary dictionary_; ///< Holds the ID to text lookups
+ const std::string empty_; ///< Empty string
};
} // namespace log