mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 13:07:50 +00:00
[master] Merge branch 'trac4489'
This commit is contained in:
commit
0d8dc7affb
@ -4,7 +4,7 @@ EXTRA_DIST = version.ent.in Doxyfile Doxyfile-xml
|
|||||||
EXTRA_DIST += devel/config-backend.dox
|
EXTRA_DIST += devel/config-backend.dox
|
||||||
EXTRA_DIST += devel/contribute.dox
|
EXTRA_DIST += devel/contribute.dox
|
||||||
EXTRA_DIST += devel/mainpage.dox
|
EXTRA_DIST += devel/mainpage.dox
|
||||||
EXTRA_DIST += devel/qa.dox
|
EXTRA_DIST += devel/unit-tests.dox
|
||||||
|
|
||||||
nobase_dist_doc_DATA = examples/ddns/sample1.json
|
nobase_dist_doc_DATA = examples/ddns/sample1.json
|
||||||
nobase_dist_doc_DATA += examples/ddns/template.json
|
nobase_dist_doc_DATA += examples/ddns/template.json
|
||||||
|
@ -94,7 +94,7 @@ written and observing the test fail, you can detect the situation
|
|||||||
where a bug in the test code will cause it to pass regardless of
|
where a bug in the test code will cause it to pass regardless of
|
||||||
the code being tested.
|
the code being tested.
|
||||||
|
|
||||||
See @ref qaUnitTests for instructions on how to run unit-tests.
|
See @ref unitTests for instructions on how to run unit-tests.
|
||||||
|
|
||||||
If you happen to add new files or have modified any \c Makefile.am
|
If you happen to add new files or have modified any \c Makefile.am
|
||||||
files, it is also a good idea to check if you haven't broken the
|
files, it is also a good idea to check if you haven't broken the
|
||||||
|
@ -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
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -34,6 +34,12 @@
|
|||||||
* @section contrib Contributor's Guide
|
* @section contrib Contributor's Guide
|
||||||
* - @subpage contributorGuide
|
* - @subpage contributorGuide
|
||||||
*
|
*
|
||||||
|
* @section buildingKeaWithUnitTests Building Kea with Unit tests
|
||||||
|
* - @subpage unitTests
|
||||||
|
* - @subpage unitTestsIntroduction
|
||||||
|
* - @subpage unitTestsEnvironmentVariables
|
||||||
|
* - @subpage unitTestsDatabaseConfig
|
||||||
|
*
|
||||||
* @section hooksFramework Hooks Framework
|
* @section hooksFramework Hooks Framework
|
||||||
* - @subpage hooksdgDevelopersGuide
|
* - @subpage hooksdgDevelopersGuide
|
||||||
* - @subpage dhcpv4Hooks
|
* - @subpage dhcpv4Hooks
|
||||||
@ -107,9 +113,6 @@
|
|||||||
* - @subpage configBackendAdding
|
* - @subpage configBackendAdding
|
||||||
* - @subpage perfdhcpInternals
|
* - @subpage perfdhcpInternals
|
||||||
*
|
*
|
||||||
* @section qualityAssurance Quality Assurance
|
|
||||||
* - @subpage qaUnitTests
|
|
||||||
*
|
|
||||||
* @section miscellaneousTopics Miscellaneous Topics
|
* @section miscellaneousTopics Miscellaneous Topics
|
||||||
* - @subpage logKeaLogging
|
* - @subpage logKeaLogging
|
||||||
* - @subpage logBasicIdeas
|
* - @subpage logBasicIdeas
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
// Copyright (C) 2015 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/.
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
@page qa Kea Quality Assurance processes
|
|
||||||
|
|
||||||
@section qaUnitTests Unit-tests
|
|
||||||
|
|
||||||
Kea uses the Google C++ Testing Framework (also called googletest or gtest) as a
|
|
||||||
base for our C++ unit-tests. See http://code.google.com/p/googletest/ for
|
|
||||||
details. We used to have Python unit-tests that were inherited from BIND10
|
|
||||||
days. Those tests are removed now, so please do not develop any new Python
|
|
||||||
tests in Kea. If you want to write DHCP tests in Python, we encourage you to
|
|
||||||
take a look at ISC Forge: http://kea.isc.org/wiki/IscForge. You must have \c
|
|
||||||
gtest installed or at least extracted in a directory before compiling Kea
|
|
||||||
unit-tests. To enable unit-tests in Kea, use:
|
|
||||||
|
|
||||||
@code
|
|
||||||
./configure --with-gtest=/path/to/your/gtest/dir
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
@code
|
|
||||||
./configure --with-gtest-source=/path/to/your/gtest/dir
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Depending on how you compiled or installed \c gtest (e.g. from sources
|
|
||||||
or using some package management system) one of those two switches will
|
|
||||||
find \c gtest. After that you make run unit-tests:
|
|
||||||
|
|
||||||
@code
|
|
||||||
make check
|
|
||||||
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
The following environment variable can affect unit-tests:
|
|
||||||
|
|
||||||
- KEA_LOCKFILE_DIR - Specifies a directory where the logging system should
|
|
||||||
create its lock file. If not specified, it is prefix/var/run/kea, where prefix
|
|
||||||
defaults to /usr/local. This variable must not end with a slash. There is one
|
|
||||||
special value: "none", which instructs Kea to not create lock file at
|
|
||||||
all. This may cause issues if several processes log to the same file.
|
|
||||||
Also see Kea User's Guide, section 15.3.
|
|
||||||
|
|
||||||
- KEA_LOGGER_DESTINATION - Specifies logging destination. If not set, logged
|
|
||||||
messages will not be recorded anywhere. There are 3 special values:
|
|
||||||
stdout, stderr and syslog. Any other value is interpreted as a filename.
|
|
||||||
Also see Kea User's Guide, section 15.3.
|
|
||||||
|
|
||||||
- KEA_PIDFILE_DIR - Specifies the directory which should be used for PID files
|
|
||||||
as used by dhcp::Daemon or its derivatives. If not specified, the default is
|
|
||||||
prefix/var/run/kea, where prefix defaults to /usr/local. This variable must
|
|
||||||
not end with a slash.
|
|
||||||
|
|
||||||
- KEA_SOCKET_TEST_DIR - if set, it specifies the directory where Unix
|
|
||||||
sockets are created. There's OS limitation on how long a Unix socket
|
|
||||||
path can be. It is typcially slightly over 100 characters. If you
|
|
||||||
happen to build and run unit-tests in deeply nested directories, this
|
|
||||||
may become a problem. KEA_SOCKET_TEST_DIR can be specified to instruct
|
|
||||||
unit-test to use a different directory. Must not end with slash (e.g.
|
|
||||||
/tmp).
|
|
||||||
|
|
||||||
*/
|
|
264
doc/devel/unit-tests.dox
Normal file
264
doc/devel/unit-tests.dox
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
// 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/.
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
@page unitTests Building Kea with Unit Tests
|
||||||
|
|
||||||
|
@section unitTestsIntroduction Introduction
|
||||||
|
|
||||||
|
Kea uses the Google C++ Testing Framework (also called googletest or gtest) as a
|
||||||
|
base for our C++ unit-tests. See http://code.google.com/p/googletest/ for
|
||||||
|
details. We used to have Python unit-tests inherited from BIND10
|
||||||
|
days but have been removed, so please do not write any new Kea unit
|
||||||
|
tests in Python. (If you want to write DHCP tests in Python, we encourage you to
|
||||||
|
take a look at ISC Forge: http://kea.isc.org/wiki/IscForge.)
|
||||||
|
|
||||||
|
You must have \c gtest installed or at least extracted in a directory
|
||||||
|
before compiling Kea unit-tests. To enable unit-tests in Kea, use:
|
||||||
|
|
||||||
|
@code
|
||||||
|
./configure --with-gtest=/path/to/your/gtest/dir
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
@code
|
||||||
|
./configure --with-gtest-source=/path/to/your/gtest/dir
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Depending on how you compiled or installed \c gtest (e.g. from sources
|
||||||
|
or using some package management system) one of those two switches will
|
||||||
|
find \c gtest. After that you make and run the unit-tests with:
|
||||||
|
|
||||||
|
@code
|
||||||
|
make check
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@section unitTestsEnvironmentVariables Environment Variables
|
||||||
|
|
||||||
|
The following environment variable can affect the unit tests:
|
||||||
|
|
||||||
|
- KEA_LOCKFILE_DIR - Specifies a directory where the logging system should
|
||||||
|
create its lock file. If not specified, it is <i>prefix</i>/var/run/kea,
|
||||||
|
where <i>prefix</i> defaults to /usr/local. This variable must not end
|
||||||
|
with a slash. There is one special value, "none", which instructs Kea to
|
||||||
|
not create a lock file at all. This may cause issues if several processes
|
||||||
|
log to the same file. (Also see the Kea User's Guide, section 15.3.)
|
||||||
|
|
||||||
|
- KEA_LOGGER_DESTINATION - Specifies the logging destination. If not set, logged
|
||||||
|
messages will not be recorded anywhere. There are three special values:
|
||||||
|
stdout, stderr and syslog. Any other value is interpreted as a filename.
|
||||||
|
(Also see Kea User's Guide, section 15.3.)
|
||||||
|
|
||||||
|
- KEA_PIDFILE_DIR - Specifies the directory which should be used for PID files
|
||||||
|
as used by dhcp::Daemon or its derivatives. If not specified, the
|
||||||
|
default is <i>prefix</i>/var/run/kea, where <i>prefix</i> defaults to
|
||||||
|
/usr/local. This variable must not end with a slash.
|
||||||
|
|
||||||
|
- KEA_SOCKET_TEST_DIR - if set, it specifies the directory where Unix
|
||||||
|
sockets are created. There is an operating system limitation on how
|
||||||
|
long a Unix socket path can be, typically slightly over 100
|
||||||
|
characters. If you happen to build and run unit-tests in deeply nested
|
||||||
|
directories, this may become a problem. KEA_SOCKET_TEST_DIR can be
|
||||||
|
specified to instruct unit-test to use a different directory. It must
|
||||||
|
not end with slash.
|
||||||
|
|
||||||
|
@section unitTestsDatabaseConfig Databases Configuration for 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 unitTestsDatabaseUsers Database Users Required for Unit Tests
|
||||||
|
|
||||||
|
Unit tests validating database backends require that the <i>keatest</i>
|
||||||
|
database is created. This database should be empty. The unit tests
|
||||||
|
also require that the <i>keatest</i> user is created and that this user
|
||||||
|
is configured to access the database with a password of <i>keatest</i>.
|
||||||
|
Unit tests use these credentials to create database schema, run test cases
|
||||||
|
and drop the schema. Thus, the <i>keatest</i> user must have sufficiently
|
||||||
|
high privileges to create and drop tables, as well as insert and modify the
|
||||||
|
data within those tables.
|
||||||
|
|
||||||
|
The database backends which support read only access to the host
|
||||||
|
reservations databases (currently MySQL and PostgreSQL) include unit
|
||||||
|
tests verifying that a database user with read-only privileges can be
|
||||||
|
used to retrieve host reservations. Those tests require another user,
|
||||||
|
<i>keatest_readonly</i>, with SQL SELECT privilege to the <i>keatest</i>
|
||||||
|
database (i.e. without INSERT, UPDATE etc.), is also created.
|
||||||
|
<i>keatest_readonly</i> should also have the password <i>keatest</i>.
|
||||||
|
|
||||||
|
The following sections provide step-by-step guidelines how to setup the
|
||||||
|
databases for running unit tests.
|
||||||
|
|
||||||
|
@subsection mysqlUnitTestsPrerequisites MySQL Database
|
||||||
|
|
||||||
|
The steps to create the database and users 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 users under which the test client will connect to the database
|
||||||
|
(the apostrophes around the words <i>keatest</i>, <i>keatest_readonly</i>, and
|
||||||
|
<i>localhost</i> are required):
|
||||||
|
@verbatim
|
||||||
|
mysql> CREATE USER 'keatest'@'localhost' IDENTIFIED BY 'keatest';
|
||||||
|
mysql> CREATE USER 'keatest_readonly'@'localhost' IDENTIFIED BY 'keatest';
|
||||||
|
mysql>@endverbatim\n
|
||||||
|
-# Grant the created users permissions to access the <i>keatest</i> database
|
||||||
|
(again, the apostrophes around the user names and <i>localhost</i>
|
||||||
|
are required):
|
||||||
|
@verbatim
|
||||||
|
mysql> GRANT ALL ON keatest.* TO 'keatest'@'localhost';
|
||||||
|
mysql> GRANT SELECT ON keatest.* TO 'keatest_readonly'@'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 \c --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 pgsqlUnitTestsPrerequisites PostgreSQL Database
|
||||||
|
|
||||||
|
PostgreSQL set up differs from system to system. Please consult your
|
||||||
|
operating system-specific PostgreSQL documentation. The remainder of
|
||||||
|
that section uses Ubuntu 13.10 x64 (with PostgreSQL 9.0+) as an example.
|
||||||
|
|
||||||
|
On Ubuntu, PostgreSQL is installed (with <tt>sudo apt-get install
|
||||||
|
postgresql</tt>) under user <i>postgres</i>. To create new databases
|
||||||
|
or add new users, initial commands must be issued under this username:
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
PostgreSQL versions earlier than 9.0 don't provide an SQL statement for granting
|
||||||
|
privileges on all tables in a database. In newer PostgreSQL versions, it is
|
||||||
|
possible to grant specific privileges on all tables within a schema.
|
||||||
|
However, this only affects tables which exist when the privileges are granted.
|
||||||
|
To ensure that the user has specific privileges to tables dynamically created
|
||||||
|
by the unit tests, the default schema privileges must be altered.
|
||||||
|
|
||||||
|
The following example demonstrates how to create the user <i>keatest_readonly</i>,
|
||||||
|
which has SELECT privilege to the tables within the <i>keatest</i> database,
|
||||||
|
in Postgres 9.0+. For earlier versions of Postgres, it is recommended to
|
||||||
|
simply grant full privileges to <i>keatest_readonly</i>, using the
|
||||||
|
same steps as for the <i>keatest</i> user.
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
$ psql -U postgres
|
||||||
|
Password for user postgres:
|
||||||
|
psql (9.1.12)
|
||||||
|
Type "help" for help.
|
||||||
|
|
||||||
|
postgres=# CREATE USER keatest_readonly WITH PASSWORD 'keatest';
|
||||||
|
CREATE ROLE
|
||||||
|
postgres=# \q
|
||||||
|
|
||||||
|
$ psql -U keatest
|
||||||
|
Password for user keatest:
|
||||||
|
psql (9.1.12)
|
||||||
|
Type "help" for help.
|
||||||
|
|
||||||
|
keatest=> ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES to keatest_readonly;
|
||||||
|
ALTER DEFAULT PRIVILEGES
|
||||||
|
keatest=> \q
|
||||||
|
@endverbatim
|
||||||
|
|
||||||
|
Note that the <i>keatest</i> user (rather than <i>postgres</i>) is used to grant
|
||||||
|
privileges to the <i>keatest_readonly</i> user. This ensures that the SELECT
|
||||||
|
privilege is granted only on the tables that the <i>keatest</i> user can access
|
||||||
|
within the public schema.
|
||||||
|
|
||||||
|
Now we should be able to log into the newly created database using both user
|
||||||
|
names:
|
||||||
|
@verbatim
|
||||||
|
$ psql -d keatest -U keatest
|
||||||
|
Password for user keatest:
|
||||||
|
psql (9.1.12)
|
||||||
|
Type "help" for help.
|
||||||
|
|
||||||
|
keatest=> \q
|
||||||
|
|
||||||
|
$ psql -d keatest -U keatest_readonly
|
||||||
|
Password for user keatest_readonly:
|
||||||
|
psql (9.1.12)
|
||||||
|
Type "help" for help.
|
||||||
|
|
||||||
|
keatest=>
|
||||||
|
@endverbatim
|
||||||
|
|
||||||
|
If instead of seeing keatest=> prompt, your login is refused with an error
|
||||||
|
code about failed peer or indent authentication, it means that PostgreSQL is
|
||||||
|
configured to check unix username and reject login attempts if PostgreSQL names
|
||||||
|
are different. To alter that, the PostgreSQL configuration must be changed -
|
||||||
|
the <tt>/etc/postgresql/9.1/main/pg_hba.conf</tt> config file
|
||||||
|
has to be altered. (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
|
||||||
|
|
||||||
|
need to be 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 that you get no password prompt. This is
|
||||||
|
most probably 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 this is a highly unsafe
|
||||||
|
configuration. The solution is the same, i.e., require password or
|
||||||
|
md5 authentication method.
|
||||||
|
|
||||||
|
If you lose the postgres user access you can first add:
|
||||||
|
@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 \c --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>).
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
@ -11,11 +11,13 @@
|
|||||||
<title>Databases and Database Version Numbers</title>
|
<title>Databases and Database Version Numbers</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Kea stores leases in one of several supported databases.
|
Kea supports storing leases and host reservations (i.e. static
|
||||||
As future versions of Kea are released, the structure of those
|
assignments of addresses, prefixes and options) in one of
|
||||||
databases will change. For example, Kea currently only stores
|
the several supported databases. As future versions of Kea
|
||||||
lease information: in the future, additional data - such as host
|
are released, the structure of those databases will change.
|
||||||
reservation details - will also be stored.
|
For example, Kea currently only stores lease information
|
||||||
|
and host reservations: in the future, additional
|
||||||
|
data - such as subnet definitions - will also be stored.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -710,6 +712,18 @@ $ <userinput>kea-admin lease-upgrade cql -n <replaceable>database-name</replacea
|
|||||||
</section>
|
</section>
|
||||||
</section> <!-- end of CQL sections -->
|
</section> <!-- end of CQL sections -->
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Using read only databases with host reservations</title>
|
||||||
|
<para>If read only database is used for storing host reservations,
|
||||||
|
Kea must be explicitly configured to operate on the database in
|
||||||
|
the read only mode.
|
||||||
|
Sections <xref linkend="read-only-database-configuration4"/> and
|
||||||
|
<xref linkend="read-only-database-configuration6"/> describe when
|
||||||
|
such configuration may be reqired and how to configure Kea to
|
||||||
|
operate using read only host database.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Limitations related to the use of the SQL databases</title>
|
<title>Limitations related to the use of the SQL databases</title>
|
||||||
|
|
||||||
@ -726,6 +740,7 @@ $ <userinput>kea-admin lease-upgrade cql -n <replaceable>database-name</replacea
|
|||||||
2147483647 seconds since the beginning of the epoch (around year 2038).
|
2147483647 seconds since the beginning of the epoch (around year 2038).
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</section> <!-- End of Database sections -->
|
</section> <!-- End of Database sections -->
|
||||||
|
|
||||||
</chapter>
|
</chapter>
|
||||||
|
@ -537,6 +537,38 @@ If a timeout is given though, it should be an integer greater than zero.
|
|||||||
If there is no password to the account, set the password to the empty string
|
If there is no password to the account, set the password to the empty string
|
||||||
"". (This is also the default.)</para>
|
"". (This is also the default.)</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="read-only-database-configuration4">
|
||||||
|
<title>Using Read-Only Databases for Host Reservations</title>
|
||||||
|
<para>
|
||||||
|
In some deployments the database user whose name is specified in the database backend
|
||||||
|
configuration may not have write privileges to the database. This is often
|
||||||
|
required by the policy within a given network to secure the data from being
|
||||||
|
unintentionally modified. In many cases administrators have inventory databases
|
||||||
|
deployed, which contain substantially more information about the hosts than
|
||||||
|
static reservations assigned to them. The inventory database can be used to create
|
||||||
|
a view of a Kea hosts database and such view is often read only.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Kea host database backends operate with an implicit configuration to both
|
||||||
|
read from and write to the database. If the database user does not have
|
||||||
|
write access to the host database, the backend will fail to start and the
|
||||||
|
server will refuse to start (or reconfigure). However, if access to a read
|
||||||
|
only host database is required for retrieving reservations for clients
|
||||||
|
and/or assign specific addresses and options, it is possible to explicitly
|
||||||
|
configure Kea to start in "read-only" mode. This is controlled by the
|
||||||
|
<command>readonly</command> boolean parameter as follows:
|
||||||
|
<screen>
|
||||||
|
"Dhcp4": { "hosts-database": { <userinput>"readonly": true</userinput>, ... }, ... }
|
||||||
|
</screen>
|
||||||
|
Setting this parameter to <userinput>false</userinput> would configure the
|
||||||
|
database backend to operate in "read-write" mode, which is also a default
|
||||||
|
configuration if the parameter is not specified.
|
||||||
|
</para>
|
||||||
|
<note><para>The <command>readonly</command> parameter is currently only supported
|
||||||
|
for MySQL and PostgreSQL databases.</para></note>
|
||||||
|
</section>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="dhcp4-interface-configuration">
|
<section id="dhcp4-interface-configuration">
|
||||||
|
@ -477,6 +477,7 @@ If a timeout is given though, it should be an integer greater than zero.
|
|||||||
If there is no password to the account, set the password to the empty string
|
If there is no password to the account, set the password to the empty string
|
||||||
"". (This is also the default.)</para>
|
"". (This is also the default.)</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="hosts6-storage">
|
<section id="hosts6-storage">
|
||||||
@ -537,8 +538,40 @@ If a timeout is given though, it should be an integer greater than zero.
|
|||||||
If there is no password to the account, set the password to the empty string
|
If there is no password to the account, set the password to the empty string
|
||||||
"". (This is also the default.)</para>
|
"". (This is also the default.)</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="read-only-database-configuration6">
|
||||||
|
<title>Using Read-Only Databases for Host Reservations</title>
|
||||||
|
<para>
|
||||||
|
In some deployments the database user whose name is specified in the database backend
|
||||||
|
configuration may not have write privileges to the database. This is often
|
||||||
|
required by the policy within a given network to secure the data from being
|
||||||
|
unintentionally modified. In many cases administrators have inventory databases
|
||||||
|
deployed, which contain substantially more information about the hosts than
|
||||||
|
static reservations assigned to them. The inventory database can be used to create
|
||||||
|
a view of a Kea hosts database and such view is often read only.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Kea host database backends operate with an implicit configuration to both
|
||||||
|
read from and write to the database. If the database user does not have
|
||||||
|
write access to the host database, the backend will fail to start and the
|
||||||
|
server will refuse to start (or reconfigure). However, if access to a read
|
||||||
|
only host database is required for retrieving reservations for clients
|
||||||
|
and/or assign specific addresses and options, it is possible to explicitly
|
||||||
|
configure Kea to start in "read-only" mode. This is controlled by the
|
||||||
|
<command>readonly</command> boolean parameter as follows:
|
||||||
|
<screen>
|
||||||
|
"Dhcp6": { "hosts-database": { <userinput>"readonly": true</userinput>, ... }, ... }
|
||||||
|
</screen>
|
||||||
|
Setting this parameter to <userinput>false</userinput> would configure the
|
||||||
|
database backend to operate in "read-write" mode, which is also a default
|
||||||
|
configuration if the parameter is not specified.
|
||||||
|
</para>
|
||||||
|
<note><para>The <command>readonly</command> parameter is currently only supported
|
||||||
|
for MySQL and PostgreSQL databases.</para></note>
|
||||||
</section>
|
</section>
|
||||||
-->
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<section id="dhcp6-interface-selection">
|
<section id="dhcp6-interface-selection">
|
||||||
<title>Interface selection</title>
|
<title>Interface selection</title>
|
||||||
|
@ -459,6 +459,17 @@ Debian and Ubuntu:
|
|||||||
Kea is built. This section covers the building of Kea with MySQL and/or PostgreSQL
|
Kea is built. This section covers the building of Kea with MySQL and/or PostgreSQL
|
||||||
and the creation of the lease database.
|
and the creation of the lease database.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<simpara>
|
||||||
|
When unit tests are built with Kea (--with-gtest configuration option is specified),
|
||||||
|
the databases must be manually pre-configured for the unit tests to run.
|
||||||
|
The details of this configuration can be found in the
|
||||||
|
<ulink url="http://git.kea.isc.org/~tester/kea/doxygen">Kea Developer's
|
||||||
|
Guide</ulink>.
|
||||||
|
</simpara>
|
||||||
|
</note>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Building with MySQL Support</title>
|
<title>Building with MySQL Support</title>
|
||||||
<para>
|
<para>
|
||||||
|
@ -275,6 +275,12 @@
|
|||||||
"item_type": "integer",
|
"item_type": "integer",
|
||||||
"item_optional": true,
|
"item_optional": true,
|
||||||
"item_default": 0
|
"item_default": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_name": "readonly",
|
||||||
|
"item_type": "boolean",
|
||||||
|
"item_optional": true,
|
||||||
|
"item_default": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -302,6 +302,12 @@
|
|||||||
"item_type": "integer",
|
"item_type": "integer",
|
||||||
"item_optional": true,
|
"item_optional": true,
|
||||||
"item_default": 0
|
"item_default": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_name": "readonly",
|
||||||
|
"item_type": "boolean",
|
||||||
|
"item_optional": true,
|
||||||
|
"item_default": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -83,137 +83,5 @@
|
|||||||
- <b>user</b> - database user ID under which the database is accessed. If not
|
- <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.
|
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>).
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
// 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
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
#include <dhcpsrv/database_connection.h>
|
#include <dhcpsrv/database_connection.h>
|
||||||
|
#include <dhcpsrv/db_exceptions.h>
|
||||||
#include <dhcpsrv/dhcpsrv_log.h>
|
#include <dhcpsrv/dhcpsrv_log.h>
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
@ -86,5 +87,24 @@ DatabaseConnection::redactedAccessString(const ParameterMap& parameters) {
|
|||||||
return (access);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -54,6 +54,15 @@ public:
|
|||||||
isc::Exception(file, line, what) {}
|
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.
|
/// @brief Common database connection class.
|
||||||
///
|
///
|
||||||
@ -112,6 +121,14 @@ public:
|
|||||||
/// @return Redacted database access string.
|
/// @return Redacted database access string.
|
||||||
static std::string redactedAccessString(const ParameterMap& parameters);
|
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:
|
private:
|
||||||
|
|
||||||
/// @brief List of parameters passed in dbconfig
|
/// @brief List of parameters passed in dbconfig
|
||||||
|
@ -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
|
// 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
|
// 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) {}
|
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) {}
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
A debug message issued when the server is about to obtain schema version
|
||||||
information from the MySQL hosts database.
|
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
|
% DHCPSRV_MYSQL_ROLLBACK rolling back MySQL database
|
||||||
The code has issued a rollback call. All outstanding transaction will
|
The code has issued a rollback call. All outstanding transaction will
|
||||||
be rolled back and not committed to the database.
|
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
|
connection including database name and username needed to access it
|
||||||
(but not the password if any) are logged.
|
(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
|
% DHCPSRV_PGSQL_ROLLBACK rolling back PostgreSQL database
|
||||||
The code has issued a rollback call. All outstanding transaction will
|
The code has issued a rollback call. All outstanding transaction will
|
||||||
be rolled back and not committed to the database.
|
be rolled back and not committed to the database.
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@ -213,20 +212,24 @@ MySqlConnection::prepareStatement(uint32_t index, const char* text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MySqlConnection::prepareStatements(const TaggedStatement tagged_statements[],
|
MySqlConnection::prepareStatements(const TaggedStatement* start_statement,
|
||||||
size_t num_statements) {
|
const TaggedStatement* end_statement) {
|
||||||
// Allocate space for all statements
|
|
||||||
statements_.clear();
|
|
||||||
statements_.resize(num_statements, NULL);
|
|
||||||
|
|
||||||
text_statements_.clear();
|
|
||||||
text_statements_.resize(num_statements, std::string(""));
|
|
||||||
|
|
||||||
// Created the MySQL prepared statements for each DML statement.
|
// Created the MySQL prepared statements for each DML statement.
|
||||||
for (int i = 0; tagged_statements[i].text != NULL; ++i) {
|
for (const TaggedStatement* tagged_statement = start_statement;
|
||||||
prepareStatement(tagged_statements[i].index,
|
tagged_statement != end_statement; ++tagged_statement) {
|
||||||
tagged_statements[i].text);
|
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
|
/// @brief Destructor
|
||||||
|
@ -240,15 +240,21 @@ public:
|
|||||||
///
|
///
|
||||||
/// Creates the prepared statements for all of the SQL statements used
|
/// Creates the prepared statements for all of the SQL statements used
|
||||||
/// by the MySQL backend.
|
/// 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
|
/// @throw isc::dhcp::DbOperationError An operation on the open database has
|
||||||
/// failed.
|
/// failed.
|
||||||
/// @throw isc::InvalidParameter 'index' is not valid for the vector. This
|
/// @throw isc::InvalidParameter 'index' is not valid for the vector. This
|
||||||
/// represents an internal error within the code.
|
/// represents an internal error within the code.
|
||||||
void prepareStatements(const TaggedStatement tagged_statements[],
|
void prepareStatements(const TaggedStatement* start_statement,
|
||||||
size_t num_statements);
|
const TaggedStatement* end_statement);
|
||||||
|
|
||||||
|
/// @brief Clears prepared statements and text statements.
|
||||||
|
void clearStatements();
|
||||||
|
|
||||||
/// @brief Open Database
|
/// @brief Open Database
|
||||||
///
|
///
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <dhcp/option_definition.h>
|
#include <dhcp/option_definition.h>
|
||||||
#include <dhcp/option_space.h>
|
#include <dhcp/option_space.h>
|
||||||
#include <dhcpsrv/cfg_option.h>
|
#include <dhcpsrv/cfg_option.h>
|
||||||
|
#include <dhcpsrv/db_exceptions.h>
|
||||||
#include <dhcpsrv/dhcpsrv_log.h>
|
#include <dhcpsrv/dhcpsrv_log.h>
|
||||||
#include <dhcpsrv/mysql_host_data_source.h>
|
#include <dhcpsrv/mysql_host_data_source.h>
|
||||||
#include <dhcpsrv/db_exceptions.h>
|
#include <dhcpsrv/db_exceptions.h>
|
||||||
@ -19,6 +20,7 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/array.hpp>
|
||||||
#include <boost/pointer_cast.hpp>
|
#include <boost/pointer_cast.hpp>
|
||||||
#include <boost/static_assert.hpp>
|
#include <boost/static_assert.hpp>
|
||||||
|
|
||||||
@ -846,7 +848,7 @@ private:
|
|||||||
size_t start_column_;
|
size_t start_column_;
|
||||||
|
|
||||||
/// @brief Option id.
|
/// @brief Option id.
|
||||||
uint64_t option_id_;
|
uint32_t option_id_;
|
||||||
|
|
||||||
/// @brief Option code.
|
/// @brief Option code.
|
||||||
uint16_t code_;
|
uint16_t code_;
|
||||||
@ -916,7 +918,7 @@ private:
|
|||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// @brief Option id for last processed row.
|
/// @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.
|
/// @brief Pointer to the @ref OptionProcessor class.
|
||||||
@ -1113,7 +1115,7 @@ public:
|
|||||||
/// @brief Returns last fetched reservation id.
|
/// @brief Returns last fetched reservation id.
|
||||||
///
|
///
|
||||||
/// @return Reservation id or 0 if no reservation data is fetched.
|
/// @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) {
|
if (reserv_type_null_ == MLM_FALSE) {
|
||||||
return (reservation_id_);
|
return (reservation_id_);
|
||||||
}
|
}
|
||||||
@ -1251,7 +1253,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
/// @brief IPv6 reservation id.
|
/// @brief IPv6 reservation id.
|
||||||
uint64_t reservation_id_;
|
uint32_t reservation_id_;
|
||||||
|
|
||||||
/// @brief IPv6 reservation type.
|
/// @brief IPv6 reservation type.
|
||||||
uint8_t reserv_type_;
|
uint8_t reserv_type_;
|
||||||
@ -1272,7 +1274,7 @@ private:
|
|||||||
uint8_t prefix_len_;
|
uint8_t prefix_len_;
|
||||||
|
|
||||||
/// @brief IAID.
|
/// @brief IAID.
|
||||||
uint8_t iaid_;
|
uint32_t iaid_;
|
||||||
|
|
||||||
/// @name Indexes of columns holding information about IPv6 reservations.
|
/// @name Indexes of columns holding information about IPv6 reservations.
|
||||||
//@{
|
//@{
|
||||||
@ -1294,7 +1296,7 @@ private:
|
|||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// @brief Reservation id for last processed row.
|
/// @brief Reservation id for last processed row.
|
||||||
uint64_t most_recent_reservation_id_;
|
uint32_t most_recent_reservation_id_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1585,7 +1587,7 @@ private:
|
|||||||
std::vector<uint8_t> value_;
|
std::vector<uint8_t> value_;
|
||||||
|
|
||||||
/// @brief Option value length.
|
/// @brief Option value length.
|
||||||
size_t value_len_;
|
unsigned long value_len_;
|
||||||
|
|
||||||
/// @brief Formatted option value length.
|
/// @brief Formatted option value length.
|
||||||
unsigned long formatted_value_len_;
|
unsigned long formatted_value_len_;
|
||||||
@ -1594,7 +1596,7 @@ private:
|
|||||||
std::string space_;
|
std::string space_;
|
||||||
|
|
||||||
/// @brief Option space name length.
|
/// @brief Option space name length.
|
||||||
size_t space_len_;
|
unsigned long space_len_;
|
||||||
|
|
||||||
/// @brief Boolean flag indicating if the option is always returned to
|
/// @brief Boolean flag indicating if the option is always returned to
|
||||||
/// a client or only when requested.
|
/// a client or only when requested.
|
||||||
@ -1604,7 +1606,7 @@ private:
|
|||||||
std::string client_class_;
|
std::string client_class_;
|
||||||
|
|
||||||
/// @brief Length of the string holding client classes for the option.
|
/// @brief Length of the string holding client classes for the option.
|
||||||
size_t client_class_len_;
|
unsigned long client_class_len_;
|
||||||
|
|
||||||
/// @brief Subnet identifier.
|
/// @brief Subnet identifier.
|
||||||
uint32_t subnet_id_;
|
uint32_t subnet_id_;
|
||||||
@ -1630,12 +1632,11 @@ public:
|
|||||||
|
|
||||||
/// @brief Statement Tags
|
/// @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 {
|
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_DHCPID, // Gets hosts by host identifier
|
||||||
GET_HOST_ADDR, // Gets hosts by IPv4 address
|
GET_HOST_ADDR, // Gets hosts by IPv4 address
|
||||||
GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
|
GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
|
||||||
@ -1643,9 +1644,20 @@ public:
|
|||||||
GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
|
GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
|
||||||
GET_HOST_PREFIX, // Gets host by IPv6 prefix
|
GET_HOST_PREFIX, // Gets host by IPv6 prefix
|
||||||
GET_VERSION, // Obtain version number
|
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
|
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.
|
/// @brief Constructor.
|
||||||
///
|
///
|
||||||
/// This constructor opens database connection and initializes prepared
|
/// This constructor opens database connection and initializes prepared
|
||||||
@ -1751,6 +1763,15 @@ public:
|
|||||||
StatementIndex stindex,
|
StatementIndex stindex,
|
||||||
boost::shared_ptr<MySqlHostExchange> exchange) const;
|
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
|
/// @brief Pointer to the object representing an exchange which
|
||||||
/// can be used to retrieve hosts and DHCPv4 options.
|
/// can be used to retrieve hosts and DHCPv4 options.
|
||||||
boost::shared_ptr<MySqlHostWithOptionsExchange> host_exchange_;
|
boost::shared_ptr<MySqlHostWithOptionsExchange> host_exchange_;
|
||||||
@ -1776,39 +1797,18 @@ public:
|
|||||||
/// @brief MySQL connection
|
/// @brief MySQL connection
|
||||||
MySqlConnection conn_;
|
MySqlConnection conn_;
|
||||||
|
|
||||||
|
/// @brief Indicates if the database is opened in read only mode.
|
||||||
|
bool is_readonly_;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
/// @brief Array of tagged statements.
|
||||||
|
typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
|
||||||
|
TaggedStatementArray;
|
||||||
|
|
||||||
/// @brief Prepared MySQL statements used by the backend to insert and
|
/// @brief Prepared MySQL statements used by the backend to insert and
|
||||||
/// retrieve hosts from the database.
|
/// retrieve hosts from the database.
|
||||||
TaggedStatement tagged_statements[] = {
|
TaggedStatementArray 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) "
|
|
||||||
"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)"},
|
|
||||||
|
|
||||||
// Retrieves host information, IPv6 reservations and both DHCPv4 and
|
// Retrieves host information, IPv6 reservations and both DHCPv4 and
|
||||||
// DHCPv6 options associated with the host. The LEFT JOIN clause is used
|
// DHCPv6 options associated with the host. The LEFT JOIN clause is used
|
||||||
// to retrieve information from 4 different tables using a single query.
|
// to retrieve information from 4 different tables using a single query.
|
||||||
@ -1930,11 +1930,33 @@ TaggedStatement tagged_statements[] = {
|
|||||||
{MySqlHostDataSourceImpl::GET_VERSION,
|
{MySqlHostDataSourceImpl::GET_VERSION,
|
||||||
"SELECT version, minor FROM schema_version"},
|
"SELECT version, minor FROM schema_version"},
|
||||||
|
|
||||||
// Marks the end of the statements table.
|
// Inserts a host into the 'hosts' table.
|
||||||
{MySqlHostDataSourceImpl::NUM_STATEMENTS, NULL}
|
{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) "
|
||||||
|
"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::
|
||||||
MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
|
MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
|
||||||
@ -1944,23 +1966,43 @@ MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
|
|||||||
DHCP4_AND_DHCP6)),
|
DHCP4_AND_DHCP6)),
|
||||||
host_ipv6_reservation_exchange_(new MySqlIPv6ReservationExchange()),
|
host_ipv6_reservation_exchange_(new MySqlIPv6ReservationExchange()),
|
||||||
host_option_exchange_(new MySqlOptionExchange()),
|
host_option_exchange_(new MySqlOptionExchange()),
|
||||||
conn_(parameters) {
|
conn_(parameters),
|
||||||
|
is_readonly_(false) {
|
||||||
|
|
||||||
// Open the database.
|
// Open the database.
|
||||||
conn_.openDatabase();
|
conn_.openDatabase();
|
||||||
|
|
||||||
// Disable autocommit. To avoid a flush to disk on every commit, the global
|
// Enable autocommit. In case transaction is explicitly used, this
|
||||||
// parameter innodb_flush_log_at_trx_commit should be set to 2. This will
|
// setting will be overwritten for the transaction. However, there are
|
||||||
// cause the changes to be written to the log, but flushed to disk in the
|
// cases when lack of autocommit could cause transactions to hang
|
||||||
// background every second. Setting the parameter to that value will speed
|
// until commit or rollback is explicitly called. This already
|
||||||
// up the system, but at the risk of losing data if the system crashes.
|
// caused issues for some unit tests which were unable to cleanup
|
||||||
my_bool result = mysql_autocommit(conn_.mysql_, 0);
|
// 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) {
|
if (result != 0) {
|
||||||
isc_throw(DbOperationError, mysql_error(conn_.mysql_));
|
isc_throw(DbOperationError, mysql_error(conn_.mysql_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare all statements likely to be used.
|
// Prepare query statements. Those are will be only used to retrieve
|
||||||
conn_.prepareStatements(tagged_statements, NUM_STATEMENTS);
|
// 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() {
|
MySqlHostDataSourceImpl::~MySqlHostDataSourceImpl() {
|
||||||
@ -2159,6 +2201,14 @@ getHost(const SubnetID& subnet_id,
|
|||||||
return (result);
|
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::
|
||||||
MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
|
MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
|
||||||
@ -2171,6 +2221,9 @@ MySqlHostDataSource::~MySqlHostDataSource() {
|
|||||||
|
|
||||||
void
|
void
|
||||||
MySqlHostDataSource::add(const HostPtr& host) {
|
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
|
// Initiate MySQL transaction as we will have to make multiple queries
|
||||||
// to insert host information into multiple tables. If that fails on
|
// to insert host information into multiple tables. If that fails on
|
||||||
// any stage, the transaction will be rolled back by the destructor of
|
// any stage, the transaction will be rolled back by the destructor of
|
||||||
@ -2493,12 +2546,16 @@ std::pair<uint32_t, uint32_t> MySqlHostDataSource::getVersion() const {
|
|||||||
|
|
||||||
void
|
void
|
||||||
MySqlHostDataSource::commit() {
|
MySqlHostDataSource::commit() {
|
||||||
|
// If operating in read-only mode, throw exception.
|
||||||
|
impl_->checkReadOnly();
|
||||||
impl_->conn_.commit();
|
impl_->conn_.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MySqlHostDataSource::rollback() {
|
MySqlHostDataSource::rollback() {
|
||||||
|
// If operating in read-only mode, throw exception.
|
||||||
|
impl_->checkReadOnly();
|
||||||
impl_->conn_.rollback();
|
impl_->conn_.rollback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#define MYSQL_HOST_DATA_SOURCE_H
|
#define MYSQL_HOST_DATA_SOURCE_H
|
||||||
|
|
||||||
#include <dhcpsrv/base_host_data_source.h>
|
#include <dhcpsrv/base_host_data_source.h>
|
||||||
|
#include <dhcpsrv/db_exceptions.h>
|
||||||
#include <dhcpsrv/mysql_connection.h>
|
#include <dhcpsrv/mysql_connection.h>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
|
@ -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
|
// 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
|
// 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_lease_mgr.h>
|
||||||
#include <dhcpsrv/mysql_connection.h>
|
#include <dhcpsrv/mysql_connection.h>
|
||||||
|
|
||||||
|
#include <boost/array.hpp>
|
||||||
#include <boost/static_assert.hpp>
|
#include <boost/static_assert.hpp>
|
||||||
#include <mysqld_error.h>
|
#include <mysqld_error.h>
|
||||||
|
|
||||||
@ -82,7 +83,8 @@ const size_t HOSTNAME_MAX_LEN = 255;
|
|||||||
/// colon separators.
|
/// colon separators.
|
||||||
const size_t ADDRESS6_TEXT_MAX_LEN = 39;
|
const size_t ADDRESS6_TEXT_MAX_LEN = 39;
|
||||||
|
|
||||||
TaggedStatement tagged_statements[] = {
|
boost::array<TaggedStatement, MySqlLeaseMgr::NUM_STATEMENTS>
|
||||||
|
tagged_statements = { {
|
||||||
{MySqlLeaseMgr::DELETE_LEASE4,
|
{MySqlLeaseMgr::DELETE_LEASE4,
|
||||||
"DELETE FROM lease4 WHERE address = ?"},
|
"DELETE FROM lease4 WHERE address = ?"},
|
||||||
{MySqlLeaseMgr::DELETE_LEASE4_STATE_EXPIRED,
|
{MySqlLeaseMgr::DELETE_LEASE4_STATE_EXPIRED,
|
||||||
@ -204,9 +206,8 @@ TaggedStatement tagged_statements[] = {
|
|||||||
"prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
|
"prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
|
||||||
"hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
|
"hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
|
||||||
"state = ? "
|
"state = ? "
|
||||||
"WHERE address = ?"},
|
"WHERE address = ?"}
|
||||||
// End of list sentinel
|
}
|
||||||
{MySqlLeaseMgr::NUM_STATEMENTS, NULL}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -1236,7 +1237,7 @@ MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare all statements likely to be used.
|
// 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
|
// Create the exchange objects for use in exchanging data between the
|
||||||
// program and the database.
|
// program and the database.
|
||||||
|
@ -53,7 +53,7 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
|
|||||||
// 2. Update the copy with the passed keywords.
|
// 2. Update the copy with the passed keywords.
|
||||||
BOOST_FOREACH(ConfigPair param, config_value->mapValue()) {
|
BOOST_FOREACH(ConfigPair param, config_value->mapValue()) {
|
||||||
try {
|
try {
|
||||||
if (param.first == "persist") {
|
if ((param.first == "persist") || (param.first == "readonly")) {
|
||||||
values_copy[param.first] = (param.second->boolValue() ?
|
values_copy[param.first] = (param.second->boolValue() ?
|
||||||
"true" : "false");
|
"true" : "false");
|
||||||
|
|
||||||
@ -72,7 +72,8 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
|
|||||||
}
|
}
|
||||||
} catch (const isc::data::TypeError& ex) {
|
} catch (const isc::data::TypeError& ex) {
|
||||||
// Append position of the element.
|
// 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() << ")");
|
<< param.second->getPosition() << ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,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
|
void
|
||||||
PgSqlConnection::openDatabase() {
|
PgSqlConnection::openDatabase() {
|
||||||
string dbconnparameters;
|
string dbconnparameters;
|
||||||
|
@ -313,6 +313,21 @@ public:
|
|||||||
/// failed.
|
/// failed.
|
||||||
void prepareStatement(const PgSqlTaggedStatement& statement);
|
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
|
/// @brief Open Database
|
||||||
///
|
///
|
||||||
/// Opens the database using the information supplied in the parameters
|
/// Opens the database using the information supplied in the parameters
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <dhcp/option.h>
|
#include <dhcp/option.h>
|
||||||
#include <dhcp/option_definition.h>
|
#include <dhcp/option_definition.h>
|
||||||
#include <dhcp/option_space.h>
|
#include <dhcp/option_space.h>
|
||||||
|
#include <dhcpsrv/db_exceptions.h>
|
||||||
#include <dhcpsrv/cfg_option.h>
|
#include <dhcpsrv/cfg_option.h>
|
||||||
#include <dhcpsrv/dhcpsrv_log.h>
|
#include <dhcpsrv/dhcpsrv_log.h>
|
||||||
#include <dhcpsrv/pgsql_host_data_source.h>
|
#include <dhcpsrv/pgsql_host_data_source.h>
|
||||||
@ -19,6 +20,7 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/array.hpp>
|
||||||
#include <boost/pointer_cast.hpp>
|
#include <boost/pointer_cast.hpp>
|
||||||
#include <boost/static_assert.hpp>
|
#include <boost/static_assert.hpp>
|
||||||
|
|
||||||
@ -1103,12 +1105,11 @@ public:
|
|||||||
|
|
||||||
/// @brief Statement Tags
|
/// @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 {
|
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_DHCPID, // Gets hosts by host identifier
|
||||||
GET_HOST_ADDR, // Gets hosts by IPv4 address
|
GET_HOST_ADDR, // Gets hosts by IPv4 address
|
||||||
GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
|
GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
|
||||||
@ -1116,9 +1117,20 @@ public:
|
|||||||
GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
|
GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
|
||||||
GET_HOST_PREFIX, // Gets host by IPv6 prefix
|
GET_HOST_PREFIX, // Gets host by IPv6 prefix
|
||||||
GET_VERSION, // Obtain version number
|
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
|
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.
|
/// @brief Constructor.
|
||||||
///
|
///
|
||||||
/// This constructor opens database connection and initializes prepared
|
/// This constructor opens database connection and initializes prepared
|
||||||
@ -1222,6 +1234,14 @@ public:
|
|||||||
StatementIndex stindex,
|
StatementIndex stindex,
|
||||||
boost::shared_ptr<PgSqlHostExchange> exchange) const;
|
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
|
/// @brief Returns PostgreSQL schema version of the open database
|
||||||
///
|
///
|
||||||
@ -1258,60 +1278,19 @@ public:
|
|||||||
/// @brief MySQL connection
|
/// @brief MySQL connection
|
||||||
PgSqlConnection conn_;
|
PgSqlConnection conn_;
|
||||||
|
|
||||||
|
/// @brief Indicates if the database is opened in read only mode.
|
||||||
|
bool is_readonly_;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
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
|
/// @brief Prepared PosgreSQL statements used by the backend to insert and
|
||||||
/// retrieve reservation data from the database.
|
/// retrieve reservation data from the database.
|
||||||
PgSqlTaggedStatement tagged_statements[] = {
|
TaggedStatementArray tagged_statements = { {
|
||||||
// PgSqlHostDataSourceImpl::INSERT_HOST
|
|
||||||
// Inserts a host into the 'hosts' table. Returns the inserted host id.
|
|
||||||
{8,
|
|
||||||
{ 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) "
|
|
||||||
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8) 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)"
|
|
||||||
},
|
|
||||||
|
|
||||||
// PgSqlHostDataSourceImpl::GET_HOST_DHCPID
|
// PgSqlHostDataSourceImpl::GET_HOST_DHCPID
|
||||||
// Retrieves host information, IPv6 reservations and both DHCPv4 and
|
// Retrieves host information, IPv6 reservations and both DHCPv4 and
|
||||||
// DHCPv6 options associated with the host. The LEFT JOIN clause is used
|
// DHCPv6 options associated with the host. The LEFT JOIN clause is used
|
||||||
@ -1445,8 +1424,53 @@ PgSqlTaggedStatement tagged_statements[] = {
|
|||||||
"SELECT version, minor FROM schema_version"
|
"SELECT version, minor FROM schema_version"
|
||||||
},
|
},
|
||||||
|
|
||||||
// Marks the end of the statements table.
|
// PgSqlHostDataSourceImpl::INSERT_HOST
|
||||||
{0, { 0 }, NULL, NULL}
|
// Inserts a host into the 'hosts' table. Returns the inserted host id.
|
||||||
|
{8,
|
||||||
|
{ 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) "
|
||||||
|
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8) 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
|
}; // end anonymous namespace
|
||||||
@ -1459,20 +1483,27 @@ PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
|
|||||||
DHCP4_AND_DHCP6)),
|
DHCP4_AND_DHCP6)),
|
||||||
host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()),
|
host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()),
|
||||||
host_option_exchange_(new PgSqlOptionExchange()),
|
host_option_exchange_(new PgSqlOptionExchange()),
|
||||||
conn_(parameters) {
|
conn_(parameters),
|
||||||
|
is_readonly_(false) {
|
||||||
|
|
||||||
// Open the database.
|
// Open the database.
|
||||||
conn_.openDatabase();
|
conn_.openDatabase();
|
||||||
|
|
||||||
int i = 0;
|
conn_.prepareStatements(tagged_statements.begin(),
|
||||||
for( ; tagged_statements[i].text != NULL ; ++i) {
|
tagged_statements.begin() + WRITE_STMTS_BEGIN);
|
||||||
conn_.prepareStatement(tagged_statements[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just in case somebody foo-barred things
|
// Check if the backend is explicitly configured to operate with
|
||||||
if (i != NUM_STATEMENTS) {
|
// read only access to the database.
|
||||||
isc_throw(DbOpenError, "Number of statements prepared: " << i
|
is_readonly_ = conn_.configuredReadOnly();
|
||||||
<< " does not match expected count:" << NUM_STATEMENTS);
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1632,6 +1663,15 @@ std::pair<uint32_t, uint32_t> PgSqlHostDataSourceImpl::getVersion() const {
|
|||||||
return (std::make_pair<uint32_t, uint32_t>(version, minor));
|
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 *********************/
|
/*********** PgSqlHostDataSource *********************/
|
||||||
|
|
||||||
|
|
||||||
@ -1646,6 +1686,9 @@ PgSqlHostDataSource::~PgSqlHostDataSource() {
|
|||||||
|
|
||||||
void
|
void
|
||||||
PgSqlHostDataSource::add(const HostPtr& host) {
|
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
|
// Initiate PostgreSQL transaction as we will have to make multiple queries
|
||||||
// to insert host information into multiple tables. If that fails on
|
// to insert host information into multiple tables. If that fails on
|
||||||
// any stage, the transaction will be rolled back by the destructor of
|
// any stage, the transaction will be rolled back by the destructor of
|
||||||
@ -1894,5 +1937,20 @@ std::pair<uint32_t, uint32_t> PgSqlHostDataSource::getVersion() const {
|
|||||||
return(impl_->getVersion());
|
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::dhcp namespace
|
||||||
}; // end of isc namespace
|
}; // end of isc namespace
|
||||||
|
@ -273,6 +273,16 @@ public:
|
|||||||
/// has failed.
|
/// has failed.
|
||||||
virtual std::pair<uint32_t, uint32_t> getVersion() const;
|
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:
|
private:
|
||||||
|
|
||||||
/// @brief Pointer to the implementation of the @ref PgSqlHostDataSource.
|
/// @brief Pointer to the implementation of the @ref PgSqlHostDataSource.
|
||||||
|
@ -88,7 +88,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the keyword and value - make sure that they are quoted.
|
// 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.
|
// lfc-interval as they are boolean and integer respectively.
|
||||||
result += quote + keyval[i] + quote + colon + space;
|
result += quote + keyval[i] + quote + colon + space;
|
||||||
if (!quoteValue(std::string(keyval[i]))) {
|
if (!quoteValue(std::string(keyval[i]))) {
|
||||||
@ -176,7 +176,8 @@ private:
|
|||||||
/// @return true if the value of the parameter should be quoted.
|
/// @return true if the value of the parameter should be quoted.
|
||||||
bool quoteValue(const std::string& parameter) const {
|
bool quoteValue(const std::string& parameter) const {
|
||||||
return ((parameter != "persist") && (parameter != "lfc-interval") &&
|
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");
|
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
|
}; // Anonymous namespace
|
||||||
|
@ -12,8 +12,10 @@
|
|||||||
#include <dhcp/option_string.h>
|
#include <dhcp/option_string.h>
|
||||||
#include <dhcp/option_int.h>
|
#include <dhcp/option_int.h>
|
||||||
#include <dhcp/option_vendor.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/generic_host_data_source_unittest.h>
|
||||||
#include <dhcpsrv/tests/test_utils.h>
|
#include <dhcpsrv/tests/test_utils.h>
|
||||||
|
#include <dhcpsrv/testutils/schema.h>
|
||||||
#include <dhcpsrv/database_connection.h>
|
#include <dhcpsrv/database_connection.h>
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
@ -504,6 +506,55 @@ GenericHostDataSourceTest::addTestOptions(const HostPtr& host,
|
|||||||
LibDHCP::setRuntimeOptionDefs(defs);
|
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) {
|
void GenericHostDataSourceTest::testBasic4(const Host::IdentifierType& id) {
|
||||||
// Make sure we have the pointer to the host data source.
|
// Make sure we have the pointer to the host data source.
|
||||||
ASSERT_TRUE(hdsptr_);
|
ASSERT_TRUE(hdsptr_);
|
||||||
|
@ -337,6 +337,23 @@ public:
|
|||||||
/// @brief Pointer to the host data source
|
/// @brief Pointer to the host data source
|
||||||
HostDataSourcePtr hdsptr_;
|
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
|
/// @brief Test that checks that simple host with IPv4 reservation
|
||||||
/// can be inserted and later retrieved.
|
/// can be inserted and later retrieved.
|
||||||
///
|
///
|
||||||
|
@ -63,8 +63,13 @@ public:
|
|||||||
/// Rolls back all pending transactions. The deletion of myhdsptr_ will close
|
/// Rolls back all pending transactions. The deletion of myhdsptr_ will close
|
||||||
/// the database. Then reopen it and delete everything created by the test.
|
/// the database. Then reopen it and delete everything created by the test.
|
||||||
virtual ~MySqlHostDataSourceTest() {
|
virtual ~MySqlHostDataSourceTest() {
|
||||||
|
try {
|
||||||
hdsptr_->rollback();
|
hdsptr_->rollback();
|
||||||
|
} catch (...) {
|
||||||
|
// Rollback may fail if backend is in read only mode. That's ok.
|
||||||
|
}
|
||||||
HostDataSourceFactory::destroy();
|
HostDataSourceFactory::destroy();
|
||||||
|
hdsptr_.reset();
|
||||||
destroyMySQLSchema();
|
destroyMySQLSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +162,9 @@ TEST(MySqlHostDataSource, OpenDatabase) {
|
|||||||
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
|
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
|
||||||
MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)),
|
MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)),
|
||||||
DbInvalidTimeout);
|
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
|
// Check for missing parameters
|
||||||
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
|
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
|
||||||
@ -167,6 +175,8 @@ TEST(MySqlHostDataSource, OpenDatabase) {
|
|||||||
destroyMySQLSchema();
|
destroyMySQLSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Check conversion functions
|
/// @brief Check conversion functions
|
||||||
///
|
///
|
||||||
/// The server works using cltt and valid_filetime. In the database, the
|
/// The server works using cltt and valid_filetime. In the database, the
|
||||||
@ -208,6 +218,10 @@ TEST(MySqlConnection, checkTimeConversion) {
|
|||||||
EXPECT_EQ(cltt, converted_cltt);
|
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
|
// Test verifies if a host reservation can be added and later retrieved by IPv4
|
||||||
// address. Host uses hw address as identifier.
|
// address. Host uses hw address as identifier.
|
||||||
TEST_F(MySqlHostDataSourceTest, basic4HWAddr) {
|
TEST_F(MySqlHostDataSourceTest, basic4HWAddr) {
|
||||||
|
@ -64,8 +64,13 @@ public:
|
|||||||
/// close the database. Then reopen it and delete everything created by
|
/// close the database. Then reopen it and delete everything created by
|
||||||
/// the test.
|
/// the test.
|
||||||
virtual ~PgSqlHostDataSourceTest() {
|
virtual ~PgSqlHostDataSourceTest() {
|
||||||
|
try {
|
||||||
hdsptr_->rollback();
|
hdsptr_->rollback();
|
||||||
|
} catch (...) {
|
||||||
|
// Rollback may fail if backend is in read only mode. That's ok.
|
||||||
|
}
|
||||||
HostDataSourceFactory::destroy();
|
HostDataSourceFactory::destroy();
|
||||||
|
hdsptr_.reset();
|
||||||
destroyPgSQLSchema();
|
destroyPgSQLSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +173,12 @@ TEST(PgSqlHostDataSource, OpenDatabase) {
|
|||||||
destroyPgSQLSchema();
|
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
|
// Test verifies if a host reservation can be added and later retrieved by IPv4
|
||||||
// address. Host uses hw address as identifier.
|
// address. Host uses hw address as identifier.
|
||||||
TEST_F(PgSqlHostDataSourceTest, basic4HWAddr) {
|
TEST_F(PgSqlHostDataSourceTest, basic4HWAddr) {
|
||||||
|
@ -25,15 +25,19 @@ const char* INVALID_NAME = "name=invalidname";
|
|||||||
const char* VALID_HOST = "host=localhost";
|
const char* VALID_HOST = "host=localhost";
|
||||||
const char* INVALID_HOST = "host=invalidhost";
|
const char* INVALID_HOST = "host=invalidhost";
|
||||||
const char* VALID_USER = "user=keatest";
|
const char* VALID_USER = "user=keatest";
|
||||||
|
const char* VALID_READONLY_USER = "user=keatest_readonly";
|
||||||
const char* INVALID_USER = "user=invaliduser";
|
const char* INVALID_USER = "user=invaliduser";
|
||||||
const char* VALID_PASSWORD = "password=keatest";
|
const char* VALID_PASSWORD = "password=keatest";
|
||||||
const char* INVALID_PASSWORD = "password=invalid";
|
const char* INVALID_PASSWORD = "password=invalid";
|
||||||
const char* VALID_TIMEOUT = "connect-timeout=10";
|
const char* VALID_TIMEOUT = "connect-timeout=10";
|
||||||
const char* INVALID_TIMEOUT_1 = "connect-timeout=foo";
|
const char* INVALID_TIMEOUT_1 = "connect-timeout=foo";
|
||||||
const char* INVALID_TIMEOUT_2 = "connect-timeout=-17";
|
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,
|
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 = " ";
|
const string space = " ";
|
||||||
string result = "";
|
string result = "";
|
||||||
|
|
||||||
@ -75,6 +79,13 @@ string connectionString(const char* type, const char* name, const char* host,
|
|||||||
result += string(timeout);
|
result += string(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (readonly_db != NULL) {
|
||||||
|
if (! result.empty()) {
|
||||||
|
result += space;
|
||||||
|
}
|
||||||
|
result += string(readonly_db);
|
||||||
|
}
|
||||||
|
|
||||||
return (result);
|
return (result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,12 +21,16 @@ extern const char* INVALID_NAME;
|
|||||||
extern const char* VALID_HOST;
|
extern const char* VALID_HOST;
|
||||||
extern const char* INVALID_HOST;
|
extern const char* INVALID_HOST;
|
||||||
extern const char* VALID_USER;
|
extern const char* VALID_USER;
|
||||||
|
extern const char* VALID_READONLY_USER;
|
||||||
extern const char* INVALID_USER;
|
extern const char* INVALID_USER;
|
||||||
extern const char* VALID_PASSWORD;
|
extern const char* VALID_PASSWORD;
|
||||||
extern const char* INVALID_PASSWORD;
|
extern const char* INVALID_PASSWORD;
|
||||||
extern const char* VALID_TIMEOUT;
|
extern const char* VALID_TIMEOUT;
|
||||||
extern const char* INVALID_TIMEOUT_1;
|
extern const char* INVALID_TIMEOUT_1;
|
||||||
extern const char* INVALID_TIMEOUT_2;
|
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.
|
/// @brief Given a combination of strings above, produce a connection string.
|
||||||
///
|
///
|
||||||
/// @param type type of the database
|
/// @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 user username used to authenticate during connection attempt
|
||||||
/// @param password password used to authenticate during connection attempt
|
/// @param password password used to authenticate during connection attempt
|
||||||
/// @param timeout timeout used 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
|
/// @return string containing all specified parameters
|
||||||
std::string connectionString(const char* type, const char* name = NULL,
|
std::string connectionString(const char* type, const char* name = NULL,
|
||||||
const char* host = NULL, const char* user = 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);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -465,6 +465,11 @@ ALTER TABLE dhcp6_options
|
|||||||
ADD CONSTRAINT fk_dhcp6_option_scope FOREIGN KEY (scope_id)
|
ADD CONSTRAINT fk_dhcp6_option_scope FOREIGN KEY (scope_id)
|
||||||
REFERENCES dhcp_option_scope (scope_id);
|
REFERENCES dhcp_option_scope (scope_id);
|
||||||
|
|
||||||
|
# Add UNSIGNED to reservation_id
|
||||||
|
ALTER TABLE ipv6_reservations
|
||||||
|
MODIFY reservation_id INT UNSIGNED NOT NULL AUTO_INCREMENT;
|
||||||
|
|
||||||
|
|
||||||
# Update the schema version number
|
# Update the schema version number
|
||||||
UPDATE schema_version
|
UPDATE schema_version
|
||||||
SET version = '4', minor = '2';
|
SET version = '4', minor = '2';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user