From ab5759687b70ef7d3c964da0db69b2145eb543d0 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Thu, 24 May 2018 21:49:40 +0300 Subject: [PATCH 01/29] added kea config tool functionality --- .gitignore | 5 + AUTHORS | 2 +- Makefile.am | 4 +- configure.ac | 38 +- doc/.gitignore | 3 +- doc/Makefile.am | 33 + .../kea4/kea_shard/config.generic | 3 + .../kea4/kea_shard/config.json | 35 + .../kea4/kea_shard/credentials.json | 9 + .../kea4/kea_shard/servers.json | 37 + .../kea6/kea_shard/config.generic | 3 + .../kea6/kea_shard/config.json | 47 + .../kea6/kea_shard/credentials.json | 9 + .../kea6/kea_shard/servers.json | 37 + .../kea4/kea_shard/config.generic | 3 + .../kea4/kea_shard/config.json | 34 + .../kea4/kea_shard/credentials.json | 9 + .../kea4/kea_shard/servers.json | 37 + .../kea6/kea_shard/config.generic | 3 + .../kea6/kea_shard/config.json | 46 + .../kea6/kea_shard/credentials.json | 9 + .../kea6/kea_shard/servers.json | 37 + .../kea4/kea_shard/config.generic | 3 + .../kea4/kea_shard/config.json | 34 + .../kea4/kea_shard/credentials.json | 9 + .../kea4/kea_shard/servers.json | 37 + .../kea6/kea_shard/config.generic | 3 + .../kea6/kea_shard/config.json | 46 + .../kea6/kea_shard/credentials.json | 9 + .../kea6/kea_shard/servers.json | 37 + .../kea-config-tool/local/kea4/kea4-cql.conf | 14 + .../local/kea4/kea4-mysql.conf | 13 + .../local/kea4/kea4-pgsql.conf | 13 + .../kea-config-tool/local/kea6/kea6-cql.conf | 14 + .../local/kea6/kea6-mysql.conf | 13 + .../local/kea6/kea6-pgsql.conf | 13 + .../kea-config-tool/master-db-connect-cql.ini | 26 + .../master-db-connect-mysql.ini | 24 + .../master-db-connect-pgsql.ini | 24 + doc/examples/kea4/backends.json | 3 +- doc/guide/Makefile.am | 1 + doc/guide/intro.xml | 15 +- doc/guide/kea-config-tool.xml | 280 ++++ doc/guide/lfc.xml | 2 +- src/bin/Makefile.am | 2 +- src/bin/admin/admin-utils.sh | 339 ++++- src/bin/admin/kea-admin.in | 754 ++++++++++- src/bin/admin/tests/dhcpdb_create_1.0.cql | 6 +- src/bin/admin/tests/dhcpdb_create_1.0.mysql | 2 +- src/bin/agent/agent_lexer.cc | 2 +- src/bin/agent/agent_lexer.ll | 2 +- src/bin/agent/ca_command_mgr.cc | 2 + src/bin/agent/ca_log.cc | 2 + src/bin/agent/ca_response_creator.cc | 2 + src/bin/agent/parser_context.cc | 2 + src/bin/agent/simple_parser.cc | 2 + src/bin/agent/tests/ca_unittests.cc | 2 + src/bin/agent/tests/parser_unittests.cc | 2 + src/bin/d2/d2_lexer.cc | 2 +- src/bin/d2/d2_lexer.ll | 2 +- src/bin/d2/d2_simple_parser.cc | 2 + src/bin/d2/parser_context.cc | 2 + src/bin/d2/tests/parser_unittest.cc | 2 + src/bin/dhcp4/Makefile.am | 5 +- src/bin/dhcp4/ctrl_dhcp4_srv.cc | 275 +++- src/bin/dhcp4/ctrl_dhcp4_srv.h | 41 +- src/bin/dhcp4/dhcp4_lexer.ll | 107 +- src/bin/dhcp4/dhcp4_log.cc | 13 +- src/bin/dhcp4/dhcp4_log.h | 22 +- src/bin/dhcp4/dhcp4_messages.mes | 19 +- src/bin/dhcp4/dhcp4_parser.yy | 82 +- src/bin/dhcp4/dhcp4_srv.cc | 171 ++- src/bin/dhcp4/dhcp4_srv.h | 6 +- src/bin/dhcp4/json_config_parser.cc | 104 +- src/bin/dhcp4/json_config_parser.h | 30 +- src/bin/dhcp4/main.cc | 2 +- src/bin/dhcp4/parser_context.cc | 2 + src/bin/dhcp4/parser_context.h | 16 +- src/bin/dhcp4/tests/callout_library_3.cc | 2 + .../dhcp4/tests/ctrl_dhcp4_srv_unittest.cc | 2 +- src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 2 +- src/bin/dhcp4/tests/hooks_unittest.cc | 2 +- src/bin/dhcp4/tests/parser_unittest.cc | 2 + src/bin/dhcp6/Makefile.am | 4 + src/bin/dhcp6/ctrl_dhcp6_srv.cc | 279 +++- src/bin/dhcp6/ctrl_dhcp6_srv.h | 38 +- src/bin/dhcp6/dhcp6_hooks.dox | 4 +- src/bin/dhcp6/dhcp6_lexer.ll | 107 +- src/bin/dhcp6/dhcp6_log.cc | 13 +- src/bin/dhcp6/dhcp6_log.h | 22 +- src/bin/dhcp6/dhcp6_messages.mes | 12 + src/bin/dhcp6/dhcp6_parser.yy | 79 +- src/bin/dhcp6/dhcp6_srv.cc | 219 ++- src/bin/dhcp6/dhcp6_srv.h | 5 +- src/bin/dhcp6/dhcp6to4_ipc.cc | 8 +- src/bin/dhcp6/json_config_parser.cc | 102 +- src/bin/dhcp6/json_config_parser.h | 31 +- src/bin/dhcp6/main.cc | 2 +- src/bin/dhcp6/parser_context.cc | 2 + src/bin/dhcp6/parser_context.h | 16 +- src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 2 +- src/bin/dhcp6/tests/dhcp6_test_utils.cc | 6 +- src/bin/dhcp6/tests/hooks_unittest.cc | 4 +- src/bin/dhcp6/tests/parser_unittest.cc | 2 + src/bin/dhcp6/tests/rebind_unittest.cc | 3 +- .../dhcp6/tests/shared_network_unittest.cc | 2 +- src/bin/kea_config_tool/.gitignore | 5 + src/bin/kea_config_tool/Makefile.am | 79 ++ src/bin/kea_config_tool/command_options.cc | 389 ++++++ src/bin/kea_config_tool/command_options.h | 130 ++ src/bin/kea_config_tool/config-tool.xml | 193 +++ .../kea_config_tool/config_tool_controller.cc | 1204 +++++++++++++++++ .../kea_config_tool/config_tool_controller.h | 191 +++ src/bin/kea_config_tool/config_tool_log.cc | 28 + src/bin/kea_config_tool/config_tool_log.h | 33 + .../kea_config_tool/config_tool_messages.mes | 62 + src/bin/kea_config_tool/kea-config-tool.xml | 293 ++++ src/bin/kea_config_tool/kea_admin.cc | 104 ++ src/bin/kea_config_tool/kea_admin.h | 48 + src/bin/kea_config_tool/main.cc | 72 + src/bin/kea_config_tool/server_config.cc | 278 ++++ src/bin/kea_config_tool/server_config.h | 80 ++ src/bin/kea_config_tool/tests/.gitignore | 2 + src/bin/kea_config_tool/tests/Makefile.am | 38 + .../tests/command_options_helper.h | 147 ++ .../tests/command_options_unittest.cc | 681 ++++++++++ .../tests/config_tool_controller_unittest.cc | 21 + .../tests/kea_admin_unittest.cc | 21 + .../kea_config_tool/tests/run_unittests.cc | 24 + .../tests/server_config_unittest.cc | 21 + .../dhcp/lease_cmds/lease_cmds_callouts.cc | 2 + src/hooks/dhcp/lease_cmds/lease_cmds_log.cc | 2 + .../lease_cmds/tests/lease_cmds_unittest.cc | 2 + .../dhcp/lease_cmds/tests/run_unittests.cc | 2 + src/lib/asiolink/io_address.cc | 4 +- src/lib/asiolink/io_address.h | 4 +- .../testutils/test_server_unix_socket.cc | 2 + src/lib/asiolink/unix_domain_socket.cc | 2 + src/lib/cc/cfg_to_element.h | 4 +- src/lib/cc/dhcp_config_error.h | 4 +- src/lib/cc/json_feed.cc | 2 + src/lib/cc/simple_parser.cc | 2 + src/lib/cc/simple_parser.h | 4 +- src/lib/cc/user_context.cc | 2 + src/lib/config/base_command_mgr.cc | 2 + src/lib/config/client_connection.cc | 2 + src/lib/config/config_log.cc | 5 +- src/lib/config/config_log.h | 6 +- src/lib/config/hooked_command_mgr.cc | 2 + src/lib/dhcp/classify.h | 11 +- src/lib/dhcp/docsis3_option_defs.h | 7 +- src/lib/dhcp/duid.h | 4 +- src/lib/dhcp/hwaddr.h | 4 +- src/lib/dhcp/iface_mgr.cc | 9 +- src/lib/dhcp/iface_mgr.h | 21 +- src/lib/dhcp/libdhcp++.cc | 2 +- src/lib/dhcp/option_custom.cc | 2 +- src/lib/dhcp/option_data_types.h | 4 +- src/lib/dhcp/option_definition.cc | 4 +- src/lib/dhcp/option_space.h | 4 +- src/lib/dhcp/pkt.cc | 4 +- src/lib/dhcp/pkt.h | 5 +- src/lib/dhcp/pkt4.cc | 4 +- src/lib/dhcp/pkt4.h | 6 +- src/lib/dhcp/std_option_defs.h | 13 +- src/lib/dhcp/tests/libdhcp++_unittest.cc | 2 +- src/lib/dhcp/tests/option_custom_unittest.cc | 2 +- .../dhcp/tests/option_data_types_unittest.cc | 2 +- .../option_opaque_data_tuples_unittest.cc | 9 +- src/lib/dhcp_ddns/ncr_msg.h | 4 +- src/lib/dhcpsrv/Makefile.am | 11 + src/lib/dhcpsrv/alloc_engine.cc | 498 ++++--- src/lib/dhcpsrv/alloc_engine.h | 9 +- src/lib/dhcpsrv/alloc_engine_log.cc | 9 +- src/lib/dhcpsrv/alloc_engine_log.h | 12 +- src/lib/dhcpsrv/alloc_engine_messages.mes | 3 + src/lib/dhcpsrv/base_host_data_source.h | 13 +- .../cql_host_data_source_benchmark.cc | 11 +- .../benchmarks/cql_lease_mgr_benchmark.cc | 2 +- .../generic_host_data_source_benchmark.cc | 2 +- .../generic_host_data_source_benchmark.h | 2 +- .../benchmarks/generic_lease_mgr_benchmark.cc | 2 +- .../benchmarks/generic_lease_mgr_benchmark.h | 2 +- .../mysql_host_data_source_benchmark.cc | 11 +- .../benchmarks/mysql_lease_mgr_benchmark.cc | 2 +- .../pgsql_host_data_source_benchmark.cc | 11 +- .../benchmarks/pgsql_lease_mgr_benchmark.cc | 2 +- src/lib/dhcpsrv/benchmarks/run_benchmarks.cc | 4 +- src/lib/dhcpsrv/cfg_4o6.cc | 2 + src/lib/dhcpsrv/cfg_4o6.h | 4 +- src/lib/dhcpsrv/cfg_db_access.cc | 42 +- src/lib/dhcpsrv/cfg_db_access.h | 39 +- src/lib/dhcpsrv/cfg_hosts.cc | 34 +- src/lib/dhcpsrv/cfg_hosts.h | 10 +- src/lib/dhcpsrv/cfg_hosts_util.cc | 2 + src/lib/dhcpsrv/cfg_mac_source.h | 4 +- src/lib/dhcpsrv/cfg_srv_config_type.cc | 26 + src/lib/dhcpsrv/cfg_srv_config_type.h | 51 + src/lib/dhcpsrv/cfg_subnets4.cc | 4 +- src/lib/dhcpsrv/cfgmgr.cc | 8 +- src/lib/dhcpsrv/cql_connection.cc | 2 +- src/lib/dhcpsrv/cql_connection.h | 7 +- src/lib/dhcpsrv/cql_exchange.cc | 4 +- src/lib/dhcpsrv/cql_exchange.h | 2 +- src/lib/dhcpsrv/cql_host_data_source.cc | 56 +- src/lib/dhcpsrv/cql_host_data_source.h | 6 + src/lib/dhcpsrv/cql_lease_mgr.cc | 274 ++-- src/lib/dhcpsrv/cql_lease_mgr.h | 8 +- src/lib/dhcpsrv/cql_srv_config_master_mgr.cc | 922 +++++++++++++ src/lib/dhcpsrv/cql_srv_config_master_mgr.h | 244 ++++ src/lib/dhcpsrv/cql_srv_config_mgr.cc | 830 ++++++++++++ src/lib/dhcpsrv/cql_srv_config_mgr.h | 207 +++ src/lib/dhcpsrv/database_connection.h | 4 +- src/lib/dhcpsrv/db_log.cc | 2 + src/lib/dhcpsrv/db_type.h | 4 +- src/lib/dhcpsrv/dhcpsrv_db_log.cc | 2 + src/lib/dhcpsrv/dhcpsrv_log.cc | 10 +- src/lib/dhcpsrv/dhcpsrv_log.h | 14 +- src/lib/dhcpsrv/dhcpsrv_messages.mes | 280 ++++ src/lib/dhcpsrv/host_container.h | 4 +- src/lib/dhcpsrv/hosts_log.cc | 9 +- src/lib/dhcpsrv/hosts_log.h | 12 +- src/lib/dhcpsrv/lease.cc | 4 +- src/lib/dhcpsrv/lease.h | 5 +- src/lib/dhcpsrv/lease_mgr.cc | 4 +- src/lib/dhcpsrv/lease_mgr.h | 12 +- src/lib/dhcpsrv/memfile_lease_mgr.cc | 12 +- src/lib/dhcpsrv/memfile_lease_mgr.h | 6 +- src/lib/dhcpsrv/mysql_connection.cc | 11 +- src/lib/dhcpsrv/mysql_connection.h | 4 +- src/lib/dhcpsrv/mysql_host_data_source.cc | 40 +- src/lib/dhcpsrv/mysql_host_data_source.h | 8 +- src/lib/dhcpsrv/mysql_lease_mgr.cc | 32 +- src/lib/dhcpsrv/mysql_lease_mgr.h | 8 +- .../dhcpsrv/mysql_srv_config_master_mgr.cc | 981 ++++++++++++++ src/lib/dhcpsrv/mysql_srv_config_master_mgr.h | 310 +++++ src/lib/dhcpsrv/mysql_srv_config_mgr.cc | 625 +++++++++ src/lib/dhcpsrv/mysql_srv_config_mgr.h | 349 +++++ src/lib/dhcpsrv/network.cc | 2 + src/lib/dhcpsrv/network_state.cc | 2 + src/lib/dhcpsrv/parsers/dbaccess_parser.cc | 32 +- src/lib/dhcpsrv/parsers/dbaccess_parser.h | 7 +- src/lib/dhcpsrv/parsers/dhcp_parsers.cc | 10 +- src/lib/dhcpsrv/parsers/dhcp_parsers.h | 4 +- src/lib/dhcpsrv/parsers/option_data_parser.cc | 2 + .../dhcpsrv/parsers/shared_network_parser.cc | 2 + src/lib/dhcpsrv/parsers/simple_parser4.cc | 6 +- src/lib/dhcpsrv/parsers/simple_parser6.cc | 6 +- src/lib/dhcpsrv/pgsql_connection.cc | 5 +- src/lib/dhcpsrv/pgsql_host_data_source.cc | 46 +- src/lib/dhcpsrv/pgsql_host_data_source.h | 8 +- src/lib/dhcpsrv/pgsql_lease_mgr.cc | 37 +- src/lib/dhcpsrv/pgsql_lease_mgr.h | 8 +- .../dhcpsrv/pgsql_srv_config_master_mgr.cc | 648 +++++++++ src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h | 292 ++++ src/lib/dhcpsrv/pgsql_srv_config_mgr.cc | 513 +++++++ src/lib/dhcpsrv/pgsql_srv_config_mgr.h | 300 ++++ src/lib/dhcpsrv/shared_network.cc | 2 + src/lib/dhcpsrv/sql_common.h | 7 +- src/lib/dhcpsrv/srv_config.cc | 39 +- src/lib/dhcpsrv/srv_config.h | 104 +- src/lib/dhcpsrv/srv_config_master_mgr.cc | 39 + src/lib/dhcpsrv/srv_config_master_mgr.h | 175 +++ .../dhcpsrv/srv_config_master_mgr_factory.cc | 116 ++ .../dhcpsrv/srv_config_master_mgr_factory.h | 111 ++ src/lib/dhcpsrv/srv_config_mgr.cc | 39 + src/lib/dhcpsrv/srv_config_mgr.h | 190 +++ src/lib/dhcpsrv/srv_config_mgr_factory.cc | 126 ++ src/lib/dhcpsrv/srv_config_mgr_factory.h | 117 ++ src/lib/dhcpsrv/subnet.cc | 8 +- src/lib/dhcpsrv/subnet.h | 4 +- .../dhcpsrv/tests/alloc_engine4_unittest.cc | 6 +- .../dhcpsrv/tests/alloc_engine6_unittest.cc | 8 +- src/lib/dhcpsrv/tests/alloc_engine_utils.cc | 8 +- .../dhcpsrv/tests/cql_connection_unittest.cc | 12 +- .../tests/cql_host_data_source_unittest.cc | 14 +- .../dhcpsrv/tests/cql_lease_mgr_unittest.cc | 17 +- .../tests/generic_lease_mgr_unittest.cc | 16 +- .../tests/generic_lease_mgr_unittest.h | 37 +- src/lib/dhcpsrv/tests/lease_mgr_unittest.cc | 10 +- .../dhcpsrv/tests/mysql_lease_mgr_unittest.cc | 10 +- .../dhcpsrv/tests/pgsql_exchange_unittest.cc | 4 +- .../tests/pgsql_host_data_source_unittest.cc | 2 +- .../dhcpsrv/tests/pgsql_lease_mgr_unittest.cc | 10 +- src/lib/dhcpsrv/testutils/cql_schema.cc | 1 + src/lib/dhcpsrv/testutils/cql_schema.h | 33 +- src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc | 6 + src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h | 3 + .../generic_host_data_source_unittest.cc | 24 +- .../testutils/host_data_source_utils.cc | 2 + .../testutils/memory_host_data_source.h | 2 +- src/lib/dhcpsrv/testutils/mysql_schema.cc | 1 + src/lib/dhcpsrv/testutils/mysql_schema.h | 10 +- src/lib/dhcpsrv/testutils/pgsql_schema.cc | 1 + src/lib/dhcpsrv/testutils/pgsql_schema.h | 10 +- src/lib/eval/Makefile.am | 1 + src/lib/eval/eval_log.cc | 6 +- src/lib/eval/eval_log.h | 8 +- src/lib/eval/lexer.cc | 4 +- src/lib/eval/lexer.ll | 4 +- src/lib/eval/token.h | 4 +- src/lib/exceptions/exceptions.h | 22 + src/lib/hooks/hooks_config.h | 4 +- src/lib/hooks/libinfo.h | 4 +- src/lib/hooks/library_handle.h | 2 +- src/lib/hooks/tests/parking_lots_unittest.cc | 2 + src/lib/http/client.cc | 2 + src/lib/http/connection.cc | 2 + src/lib/http/connection_pool.cc | 2 + src/lib/http/date_time.cc | 2 + src/lib/http/http_header.cc | 2 + src/lib/http/http_log.cc | 2 + src/lib/http/http_message.cc | 2 + src/lib/http/http_message_parser_base.cc | 2 + src/lib/http/listener.cc | 2 + src/lib/http/post_request.cc | 2 + src/lib/http/post_request_json.cc | 2 + src/lib/http/request.cc | 2 + src/lib/http/request_parser.cc | 2 + src/lib/http/response.cc | 2 + src/lib/http/response_creator.cc | 2 + src/lib/http/response_json.cc | 2 + src/lib/http/response_parser.cc | 2 + src/lib/http/tests/run_unittests.cc | 2 + src/lib/http/url.cc | 2 + src/share/database/scripts/cql/.gitignore | 3 +- src/share/database/scripts/cql/Makefile.am | 8 +- .../scripts/cql/config_upgrade_0.0_to_1.0.cql | 33 + .../cql/config_upgrade_0.0_to_1.0.sh.in | 21 + .../database/scripts/cql/configdb_create.cql | 33 + .../database/scripts/cql/dhcpdb_drop.cql | 5 + .../scripts/cql/master_upgrade_0.0_to_1.0.cql | 41 + .../cql/master_upgrade_0.0_to_1.0.sh.in | 23 + .../database/scripts/cql/masterdb_create.cql | 41 + src/share/database/scripts/mysql/.gitignore | 2 + src/share/database/scripts/mysql/Makefile.am | 8 +- .../mysql/config_upgrade_0.0_to_1.0.mysql | 34 + .../mysql/config_upgrade_0.0_to_1.0.sh.in | 21 + .../scripts/mysql/configdb_create.mysql | 34 + .../scripts/mysql/dhcpdb_create.mysql | 1 - .../database/scripts/mysql/dhcpdb_drop.mysql | 5 + .../mysql/master_upgrade_0.0_to_1.0.mysql | 36 + .../mysql/master_upgrade_0.0_to_1.0.sh.in | 21 + .../scripts/mysql/masterdb_create.mysql | 36 + src/share/database/scripts/pgsql/.gitignore | 2 + src/share/database/scripts/pgsql/Makefile.am | 8 +- .../pgsql/config_upgrade_0.0_to_1.0.pgsql | 41 + .../pgsql/config_upgrade_0.0_to_1.0.sh.in | 21 + .../scripts/pgsql/configdb_create.pgsql | 41 + .../database/scripts/pgsql/dhcpdb_drop.pgsql | 5 + .../pgsql/master_upgrade_0.0_to_1.0.pgsql | 41 + .../pgsql/master_upgrade_0.0_to_1.0.sh.in | 21 + .../scripts/pgsql/masterdb_create.pgsql | 41 + 353 files changed, 17356 insertions(+), 1166 deletions(-) create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.generic create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.generic create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json create mode 100644 doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.generic create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.json create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.generic create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.json create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json create mode 100644 doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.generic create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.json create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.generic create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.json create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json create mode 100644 doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json create mode 100644 doc/examples/kea-config-tool/local/kea4/kea4-cql.conf create mode 100644 doc/examples/kea-config-tool/local/kea4/kea4-mysql.conf create mode 100644 doc/examples/kea-config-tool/local/kea4/kea4-pgsql.conf create mode 100644 doc/examples/kea-config-tool/local/kea6/kea6-cql.conf create mode 100644 doc/examples/kea-config-tool/local/kea6/kea6-mysql.conf create mode 100644 doc/examples/kea-config-tool/local/kea6/kea6-pgsql.conf create mode 100644 doc/examples/kea-config-tool/master-db-connect-cql.ini create mode 100644 doc/examples/kea-config-tool/master-db-connect-mysql.ini create mode 100644 doc/examples/kea-config-tool/master-db-connect-pgsql.ini create mode 100644 doc/guide/kea-config-tool.xml create mode 100644 src/bin/kea_config_tool/.gitignore create mode 100644 src/bin/kea_config_tool/Makefile.am create mode 100644 src/bin/kea_config_tool/command_options.cc create mode 100644 src/bin/kea_config_tool/command_options.h create mode 100644 src/bin/kea_config_tool/config-tool.xml create mode 100644 src/bin/kea_config_tool/config_tool_controller.cc create mode 100644 src/bin/kea_config_tool/config_tool_controller.h create mode 100644 src/bin/kea_config_tool/config_tool_log.cc create mode 100644 src/bin/kea_config_tool/config_tool_log.h create mode 100644 src/bin/kea_config_tool/config_tool_messages.mes create mode 100644 src/bin/kea_config_tool/kea-config-tool.xml create mode 100644 src/bin/kea_config_tool/kea_admin.cc create mode 100644 src/bin/kea_config_tool/kea_admin.h create mode 100644 src/bin/kea_config_tool/main.cc create mode 100644 src/bin/kea_config_tool/server_config.cc create mode 100644 src/bin/kea_config_tool/server_config.h create mode 100644 src/bin/kea_config_tool/tests/.gitignore create mode 100644 src/bin/kea_config_tool/tests/Makefile.am create mode 100644 src/bin/kea_config_tool/tests/command_options_helper.h create mode 100644 src/bin/kea_config_tool/tests/command_options_unittest.cc create mode 100644 src/bin/kea_config_tool/tests/config_tool_controller_unittest.cc create mode 100644 src/bin/kea_config_tool/tests/kea_admin_unittest.cc create mode 100644 src/bin/kea_config_tool/tests/run_unittests.cc create mode 100644 src/bin/kea_config_tool/tests/server_config_unittest.cc create mode 100644 src/lib/dhcpsrv/cfg_srv_config_type.cc create mode 100644 src/lib/dhcpsrv/cfg_srv_config_type.h create mode 100644 src/lib/dhcpsrv/cql_srv_config_master_mgr.cc create mode 100644 src/lib/dhcpsrv/cql_srv_config_master_mgr.h create mode 100644 src/lib/dhcpsrv/cql_srv_config_mgr.cc create mode 100644 src/lib/dhcpsrv/cql_srv_config_mgr.h create mode 100644 src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc create mode 100644 src/lib/dhcpsrv/mysql_srv_config_master_mgr.h create mode 100644 src/lib/dhcpsrv/mysql_srv_config_mgr.cc create mode 100644 src/lib/dhcpsrv/mysql_srv_config_mgr.h create mode 100644 src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc create mode 100644 src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h create mode 100644 src/lib/dhcpsrv/pgsql_srv_config_mgr.cc create mode 100644 src/lib/dhcpsrv/pgsql_srv_config_mgr.h create mode 100644 src/lib/dhcpsrv/srv_config_master_mgr.cc create mode 100644 src/lib/dhcpsrv/srv_config_master_mgr.h create mode 100644 src/lib/dhcpsrv/srv_config_master_mgr_factory.cc create mode 100644 src/lib/dhcpsrv/srv_config_master_mgr_factory.h create mode 100644 src/lib/dhcpsrv/srv_config_mgr.cc create mode 100644 src/lib/dhcpsrv/srv_config_mgr.h create mode 100644 src/lib/dhcpsrv/srv_config_mgr_factory.cc create mode 100644 src/lib/dhcpsrv/srv_config_mgr_factory.h create mode 100644 src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.cql create mode 100644 src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.sh.in create mode 100644 src/share/database/scripts/cql/configdb_create.cql create mode 100644 src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.cql create mode 100644 src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.sh.in create mode 100644 src/share/database/scripts/cql/masterdb_create.cql create mode 100644 src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.mysql create mode 100644 src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.sh.in create mode 100644 src/share/database/scripts/mysql/configdb_create.mysql create mode 100644 src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.mysql create mode 100755 src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.sh.in create mode 100644 src/share/database/scripts/mysql/masterdb_create.mysql create mode 100644 src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.pgsql create mode 100644 src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.sh.in create mode 100644 src/share/database/scripts/pgsql/configdb_create.pgsql create mode 100644 src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.pgsql create mode 100644 src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.sh.in create mode 100644 src/share/database/scripts/pgsql/masterdb_create.pgsql diff --git a/.gitignore b/.gitignore index 9afda8d91e..46f6973e3e 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,9 @@ config.h.in~ /test-driver /ylwrap +/.cproject +/.project +/.vscode /all.info /coverage-cpp-html /dns++.pc @@ -45,3 +48,5 @@ config.h.in~ /logger_lockfile /report.info +*.tmp +*parser.dot diff --git a/AUTHORS b/AUTHORS index cfdcb815a2..76bfd78fee 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,7 +5,7 @@ Primary developers: - Tomek Mrugalski (lead developer: DHCPv4, DHCPv6 components, prefix delegation, memfile, database interface, core libdhcp++, - host reservation, MAC extraction in DHCPv6, statistics manager, + host reservation, MAC extraction in DHCPv6, statistics manager, kea-shell) - Stephen Morris (Hooks, MySQL) - Marcin Siodelski (DHCPv4, DHCPv6 components, options handling, perfdhcp, diff --git a/Makefile.am b/Makefile.am index 2c0733c29c..c23fe48402 100644 --- a/Makefile.am +++ b/Makefile.am @@ -110,8 +110,8 @@ endif --output report.info; \ sed --in-place --expression "s|$(abs_top_srcdir)|$(abs_top_builddir)|g" report.info; \ "$(GENHTML)" --frames --show-details --title 'Kea code coverage report' --legend \ - --function-coverage --ignore-errors source --demangle-cpp \ - --output "$(OVERALL_COVERAGE_DIR)" report.info; \ + --function-coverage --ignore-errors source --demangle-cpp \ + --output "$(OVERALL_COVERAGE_DIR)" report.info; \ printf "Generated C++ code coverage report in HTML at %s.\n" "$(OVERALL_COVERAGE_DIR)"; \ else \ echo "C++ code coverage not enabled at configuration time." ; \ diff --git a/configure.ac b/configure.ac index 4e9c1cfb95..cd9c832f3d 100644 --- a/configure.ac +++ b/configure.ac @@ -62,15 +62,15 @@ AC_SUBST(SEP) # If cross compiling assume the message compiler executable was # magically already in place... if test "$cross_compiling" = "yes"; then - AC_MSG_CHECKING("build (vs. host) compiled message compiler") - if test -x "${srcdir}/src/lib/log/compiler/message"; then - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - AC_MSG_WARN("you must install a message compiler in:") - AC_MSG_WARN(" ${srcdir}/src/lib/log/compiler/message") - AC_MSG_WARN("compiled for build ($build).") - fi + AC_MSG_CHECKING("build (vs. host) compiled message compiler") + if test -x "${srcdir}/src/lib/log/compiler/message"; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + AC_MSG_WARN("you must install a message compiler in:") + AC_MSG_WARN(" ${srcdir}/src/lib/log/compiler/message") + AC_MSG_WARN("compiled for build ($build).") + fi fi AM_CONDITIONAL([CROSS_COMPILING], [test "$cross_compiling" = "yes"]) @@ -410,7 +410,7 @@ case "$host" in ;; esac if [ test $kea_undefined_pthread_behavior = "yes" ]; then - AC_DEFINE([HAS_UNDEFINED_PTHREAD_BEHAVIOR], [1], [Does this platform have some undefined pthreads behavior?]) + AC_DEFINE([HAS_UNDEFINED_PTHREAD_BEHAVIOR], [1], [Does this platform have some undefined pthreads behavior?]) fi # Our experiments have shown Solaris 10 has broken support for the @@ -816,7 +816,7 @@ if test "$CQL_CONFIG" != "" ; then CQL_INCLUDEDIR=`$CQL_CONFIG --cflags-only-I $cql_lib` CQL_CPPFLAGS="`$CQL_CONFIG --cflags-only-other $cql_lib` $CQL_INCLUDEDIR" - CQL_LIBS="`$CQL_CONFIG --libs $cql_lib`" + CQL_LIBS="`$CQL_CONFIG --libs $cql_lib`" CQL_VERSION=`$CQL_CONFIG --modversion $cql_lib` AC_SUBST(CQL_CPPFLAGS) @@ -1402,6 +1402,8 @@ AC_CONFIG_FILES([Makefile src/bin/perfdhcp/Makefile src/bin/perfdhcp/tests/Makefile src/bin/perfdhcp/tests/testdata/Makefile + src/bin/kea_config_tool/Makefile + src/bin/kea_config_tool/tests/Makefile src/bin/shell/Makefile src/bin/shell/kea-shell src/bin/shell/tests/Makefile @@ -1489,6 +1491,8 @@ AC_CONFIG_FILES([Makefile src/share/database/scripts/Makefile src/share/database/scripts/cql/Makefile src/share/database/scripts/cql/upgrade_1.0_to_2.0.sh + src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.sh + src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.sh src/share/database/scripts/mysql/Makefile src/share/database/scripts/mysql/upgrade_1.0_to_2.0.sh src/share/database/scripts/mysql/upgrade_2.0_to_3.0.sh @@ -1498,6 +1502,8 @@ AC_CONFIG_FILES([Makefile src/share/database/scripts/mysql/upgrade_5.0_to_5.1.sh src/share/database/scripts/mysql/upgrade_5.1_to_5.2.sh src/share/database/scripts/mysql/upgrade_5.2_to_6.0.sh + src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.sh + src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.sh src/share/database/scripts/pgsql/Makefile src/share/database/scripts/pgsql/upgrade_1.0_to_2.0.sh src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh @@ -1505,6 +1511,8 @@ AC_CONFIG_FILES([Makefile src/share/database/scripts/pgsql/upgrade_3.1_to_3.2.sh src/share/database/scripts/pgsql/upgrade_3.2_to_3.3.sh src/share/database/scripts/pgsql/upgrade_3.3_to_4.0.sh + src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.sh + src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.sh tools/Makefile tools/path_replacer.sh ]) @@ -1669,9 +1677,9 @@ if test "$CQL_CPPFLAGS" != "" ; then cat >> config.report << END Cassandra CQL: - CQL_VERSION: ${CQL_VERSION} - CQL_CPPFLAGS: ${CQL_CPPFLAGS} - CQL_LIBS: ${CQL_LIBS} + CQL_VERSION: ${CQL_VERSION} + CQL_CPPFLAGS: ${CQL_CPPFLAGS} + CQL_LIBS: ${CQL_LIBS} END else cat >> config.report << END @@ -1693,6 +1701,7 @@ Google Test: END else cat >> config.report << END + Google Test: no END @@ -1711,6 +1720,7 @@ Google Benchmark: END else cat >> config.report << END + Google Benchmark: no END diff --git a/doc/.gitignore b/doc/.gitignore index fd8742ed43..e7d9de716b 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,2 +1,3 @@ /version.ent -html +/html +/latex diff --git a/doc/Makefile.am b/doc/Makefile.am index 16cffb0ba0..3742f5d092 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -57,6 +57,39 @@ nobase_dist_doc_DATA += examples/kea6/simple.json nobase_dist_doc_DATA += examples/kea6/softwire46.json nobase_dist_doc_DATA += examples/kea6/stateless.json nobase_dist_doc_DATA += examples/kea6/with-ddns.json +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-mysql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-pgsql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-cql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-mysql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-pgsql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-cql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.generic +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.generic +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.generic +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.generic +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.generic +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.generic +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json +nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json +nobase_dist_doc_DATA += examples/kea-config-tool/master-db-connect-mysql.ini +nobase_dist_doc_DATA += examples/kea-config-tool/master-db-connect-pgsql.ini +nobase_dist_doc_DATA += examples/kea-config-tool/master-db-connect-cql.ini devel: mkdir -p html diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.generic b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.generic new file mode 100644 index 0000000000..3cd318ee95 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.generic @@ -0,0 +1,3 @@ +##################################################### +############### FOR FUTURE USE ##################### +##################################################### diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json new file mode 100644 index 0000000000..855842fe87 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json @@ -0,0 +1,35 @@ +{ + "Dhcp4": { + "lease-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_shard", + "max-statement-tries": 1, + "password": "", + "type": "cql", + "user": "" + }, + "expired-leases-processing": { + "flush-reclaimed-timer-wait-time": 25, + "hold-reclaimed-time": 3600, + "max-reclaim-leases": 100, + "max-reclaim-time": 250, + "reclaim-timer-wait-time": 10, + "unwarned-reclaim-cycles": 5 + }, + "valid-lifetime": 40, + "renew-timer": 10, + "subnet4": [ + { + "subnet": "192.0.2.0/24", + "pools": [ + { + "pool": "192.0.2.16 - 192.0.2.128" + } + ], + "relay": { + "ip-address": "192.0.2.1" + } + } + ] + } +} diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json new file mode 100644 index 0000000000..86b3de9994 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json @@ -0,0 +1,9 @@ +{ + "config-database": { + "user": "keatest", + "type": "cql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + } +} \ No newline at end of file diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json new file mode 100644 index 0000000000..b243fda4e2 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json @@ -0,0 +1,37 @@ +{ + "config-database": { + "user": "keatest", + "type": "cql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + }, + "master-config": [ + { + "instance-id": "kea-dhcp4-server", + "server-config": { + "Dhcp4": { + "interfaces-config": { + "interfaces": [ + "ens4" + ] + } + }, + "Logging": { + "loggers": [ + { + "name": "kea-dhcp4", + "output_options": [ + { + "output": "/var/log/kea-dhcp4.log" + } + ], + "severity": "DEBUG", + "debuglevel": 99 + } + ] + } + } + } + ] +} diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.generic b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.generic new file mode 100644 index 0000000000..3cd318ee95 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.generic @@ -0,0 +1,3 @@ +##################################################### +############### FOR FUTURE USE ##################### +##################################################### diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json new file mode 100644 index 0000000000..7cd183c5b0 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json @@ -0,0 +1,47 @@ +{ + "Dhcp6": { + "server-id": { + "htype": 1, + "identifier": "56847afe6699", + "time": 512640186, + "type": "LLT" + }, + "lease-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_shard", + "max-statement-tries": 1, + "password": "", + "type": "cql", + "user": "" + }, + "expired-leases-processing": { + "flush-reclaimed-timer-wait-time": 25, + "hold-reclaimed-time": 3600, + "max-reclaim-leases": 100, + "max-reclaim-time": 250, + "reclaim-timer-wait-time": 10, + "unwarned-reclaim-cycles": 5 + }, + "preferred-lifetime": 30, + "valid-lifetime": 40, + "renew-timer": 10, + "rebind-timer": 20, + "subnet6": [ + { + "subnet": "3001:db8:1::/48", + "pools": [ + { + "pool": "3001:db8:1::/80" + } + ], + "pd-pools": [ + { + "prefix": "3001:db8:1:1::", + "prefix-len": 80, + "delegated-len": 96 + } + ] + } + ] + } +} diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json new file mode 100644 index 0000000000..86b3de9994 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json @@ -0,0 +1,9 @@ +{ + "config-database": { + "user": "keatest", + "type": "cql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + } +} \ No newline at end of file diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json new file mode 100644 index 0000000000..0910cee47d --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json @@ -0,0 +1,37 @@ +{ + "config-database": { + "user": "keatest", + "type": "cql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + }, + "master-config": [ + { + "instance-id": "kea-dhcp6-server", + "server-config": { + "Dhcp6": { + "interfaces-config": { + "interfaces": [ + "ens4/2001:db8:1::1" + ] + } + }, + "Logging": { + "loggers": [ + { + "name": "kea-dhcp6", + "output_options": [ + { + "output": "/var/log/kea-dhcp6.log" + } + ], + "severity": "DEBUG", + "debuglevel": 99 + } + ] + } + } + } + ] +} diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.generic b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.generic new file mode 100644 index 0000000000..3cd318ee95 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.generic @@ -0,0 +1,3 @@ +##################################################### +############### FOR FUTURE USE ##################### +##################################################### diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.json new file mode 100644 index 0000000000..b6b3daae07 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.json @@ -0,0 +1,34 @@ +{ + "Dhcp4": { + "lease-database": { + "host": "127.0.0.1", + "name": "kea_shard", + "password": "keatest", + "type": "mysql", + "user": "keatest" + }, + "expired-leases-processing": { + "flush-reclaimed-timer-wait-time": 25, + "hold-reclaimed-time": 3600, + "max-reclaim-leases": 100, + "max-reclaim-time": 250, + "reclaim-timer-wait-time": 10, + "unwarned-reclaim-cycles": 5 + }, + "valid-lifetime": 40, + "renew-timer": 10, + "subnet4": [ + { + "subnet": "192.0.2.0/24", + "pools": [ + { + "pool": "192.0.2.16 - 192.0.2.128" + } + ], + "relay": { + "ip-address": "192.0.2.1" + } + } + ] + } +} diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json new file mode 100644 index 0000000000..de3f15205a --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json @@ -0,0 +1,9 @@ +{ + "config-database": { + "user": "keatest", + "type": "mysql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + } +} \ No newline at end of file diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json new file mode 100644 index 0000000000..7cd6568884 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json @@ -0,0 +1,37 @@ +{ + "config-database": { + "user": "keatest", + "type": "mysql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + }, + "master-config": [ + { + "instance-id": "kea-dhcp4-server", + "server-config": { + "Dhcp4": { + "interfaces-config": { + "interfaces": [ + "ens4" + ] + } + }, + "Logging": { + "loggers": [ + { + "name": "kea-dhcp4", + "output_options": [ + { + "output": "/var/log/kea-dhcp4.log" + } + ], + "severity": "DEBUG", + "debuglevel": 99 + } + ] + } + } + } + ] +} diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.generic b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.generic new file mode 100644 index 0000000000..3cd318ee95 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.generic @@ -0,0 +1,3 @@ +##################################################### +############### FOR FUTURE USE ##################### +##################################################### diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.json new file mode 100644 index 0000000000..56fc58dc40 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/config.json @@ -0,0 +1,46 @@ +{ + "Dhcp6": { + "server-id": { + "htype": 1, + "identifier": "56847afe6699", + "time": 512640186, + "type": "LLT" + }, + "lease-database": { + "host": "127.0.0.1", + "name": "kea_shard", + "password": "keatest", + "type": "mysql", + "user": "keatest" + }, + "expired-leases-processing": { + "flush-reclaimed-timer-wait-time": 25, + "hold-reclaimed-time": 3600, + "max-reclaim-leases": 100, + "max-reclaim-time": 250, + "reclaim-timer-wait-time": 10, + "unwarned-reclaim-cycles": 5 + }, + "preferred-lifetime": 30, + "valid-lifetime": 40, + "renew-timer": 10, + "rebind-timer": 20, + "subnet6": [ + { + "subnet": "3001:db8:1::/48", + "pools": [ + { + "pool": "3001:db8:1::/80" + } + ], + "pd-pools": [ + { + "prefix": "3001:db8:1:1::", + "prefix-len": 80, + "delegated-len": 96 + } + ] + } + ] + } +} diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json new file mode 100644 index 0000000000..de3f15205a --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json @@ -0,0 +1,9 @@ +{ + "config-database": { + "user": "keatest", + "type": "mysql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + } +} \ No newline at end of file diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json new file mode 100644 index 0000000000..7a751beded --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json @@ -0,0 +1,37 @@ +{ + "config-database": { + "user": "keatest", + "type": "mysql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + }, + "master-config": [ + { + "instance-id": "kea-dhcp6-server", + "server-config": { + "Dhcp6": { + "interfaces-config": { + "interfaces": [ + "ens4/2001:db8:1::1" + ] + } + }, + "Logging": { + "loggers": [ + { + "name": "kea-dhcp6", + "output_options": [ + { + "output": "/var/log/kea-dhcp6.log" + } + ], + "severity": "DEBUG", + "debuglevel": 99 + } + ] + } + } + } + ] +} diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.generic b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.generic new file mode 100644 index 0000000000..3cd318ee95 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.generic @@ -0,0 +1,3 @@ +##################################################### +############### FOR FUTURE USE ##################### +##################################################### diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.json new file mode 100644 index 0000000000..15d8695b54 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/config.json @@ -0,0 +1,34 @@ +{ + "Dhcp4": { + "lease-database": { + "host": "127.0.0.1", + "name": "kea_shard", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + }, + "expired-leases-processing": { + "flush-reclaimed-timer-wait-time": 25, + "hold-reclaimed-time": 3600, + "max-reclaim-leases": 100, + "max-reclaim-time": 250, + "reclaim-timer-wait-time": 10, + "unwarned-reclaim-cycles": 5 + }, + "valid-lifetime": 40, + "renew-timer": 10, + "subnet4": [ + { + "subnet": "192.0.2.0/24", + "pools": [ + { + "pool": "192.0.2.16 - 192.0.2.128" + } + ], + "relay": { + "ip-address": "192.0.2.1" + } + } + ] + } +} diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json new file mode 100644 index 0000000000..2e9ac9035c --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json @@ -0,0 +1,9 @@ +{ + "config-database": { + "user": "keatest", + "type": "postgresql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + } +} \ No newline at end of file diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json new file mode 100644 index 0000000000..c70e894edc --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json @@ -0,0 +1,37 @@ +{ + "config-database": { + "user": "keatest", + "type": "postgresql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + }, + "master-config": [ + { + "instance-id": "kea-dhcp4-server", + "server-config": { + "Dhcp4": { + "interfaces-config": { + "interfaces": [ + "ens4" + ] + } + }, + "Logging": { + "loggers": [ + { + "name": "kea-dhcp4", + "output_options": [ + { + "output": "/var/log/kea-dhcp4.log" + } + ], + "severity": "DEBUG", + "debuglevel": 99 + } + ] + } + } + } + ] +} diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.generic b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.generic new file mode 100644 index 0000000000..3cd318ee95 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.generic @@ -0,0 +1,3 @@ +##################################################### +############### FOR FUTURE USE ##################### +##################################################### diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.json new file mode 100644 index 0000000000..8ac5b1be5d --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/config.json @@ -0,0 +1,46 @@ +{ + "Dhcp6": { + "server-id": { + "htype": 1, + "identifier": "56847afe6699", + "time": 512640186, + "type": "LLT" + }, + "lease-database": { + "host": "127.0.0.1", + "name": "kea_shard", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + }, + "expired-leases-processing": { + "flush-reclaimed-timer-wait-time": 25, + "hold-reclaimed-time": 3600, + "max-reclaim-leases": 100, + "max-reclaim-time": 250, + "reclaim-timer-wait-time": 10, + "unwarned-reclaim-cycles": 5 + }, + "preferred-lifetime": 30, + "valid-lifetime": 40, + "renew-timer": 10, + "rebind-timer": 20, + "subnet6": [ + { + "subnet": "3001:db8:1::/48", + "pools": [ + { + "pool": "3001:db8:1::/80" + } + ], + "pd-pools": [ + { + "prefix": "3001:db8:1:1::", + "prefix-len": 80, + "delegated-len": 96 + } + ] + } + ] + } +} diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json new file mode 100644 index 0000000000..2e9ac9035c --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json @@ -0,0 +1,9 @@ +{ + "config-database": { + "user": "keatest", + "type": "postgresql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + } +} \ No newline at end of file diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json new file mode 100644 index 0000000000..63a10af585 --- /dev/null +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json @@ -0,0 +1,37 @@ +{ + "config-database": { + "user": "keatest", + "type": "postgresql", + "password": "keatest", + "keyspace": "kea_shard", + "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + }, + "master-config": [ + { + "instance-id": "kea-dhcp6-server", + "server-config": { + "Dhcp6": { + "interfaces-config": { + "interfaces": [ + "ens4/2001:db8:1::1" + ] + } + }, + "Logging": { + "loggers": [ + { + "name": "kea-dhcp6", + "output_options": [ + { + "output": "/var/log/kea-dhcp6.log" + } + ], + "severity": "DEBUG", + "debuglevel": 99 + } + ] + } + } + } + ] +} diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-cql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-cql.conf new file mode 100644 index 0000000000..5f84100b63 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea4/kea4-cql.conf @@ -0,0 +1,14 @@ +{ + "Dhcp4": { + "instance-id": "kea-dhcp4-server", + "configuration-type": "database", + "master-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "keatest", + "type": "cql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-mysql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-mysql.conf new file mode 100644 index 0000000000..19d00f6d75 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea4/kea4-mysql.conf @@ -0,0 +1,13 @@ +{ + "Dhcp4": { + "instance-id": "kea-dhcp4-server", + "configuration-type": "database", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-pgsql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-pgsql.conf new file mode 100644 index 0000000000..83ee23c171 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea4/kea4-pgsql.conf @@ -0,0 +1,13 @@ +{ + "Dhcp4": { + "instance-id": "kea-dhcp4-server", + "configuration-type": "database", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-cql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-cql.conf new file mode 100644 index 0000000000..eb0e92e565 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea6/kea6-cql.conf @@ -0,0 +1,14 @@ +{ + "Dhcp6": { + "instance-id": "kea-dhcp6-server", + "configuration-type": "database", + "master-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "keatest", + "type": "cql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-mysql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-mysql.conf new file mode 100644 index 0000000000..9cf67e8172 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea6/kea6-mysql.conf @@ -0,0 +1,13 @@ +{ + "Dhcp6": { + "instance-id": "kea-dhcp6-server", + "configuration-type": "database", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-pgsql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-pgsql.conf new file mode 100644 index 0000000000..b20593e4f6 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea6/kea6-pgsql.conf @@ -0,0 +1,13 @@ +{ + "Dhcp6": { + "instance-id": "kea-dhcp6-server", + "configuration-type": "database", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/master-db-connect-cql.ini b/doc/examples/kea-config-tool/master-db-connect-cql.ini new file mode 100644 index 0000000000..778550f51d --- /dev/null +++ b/doc/examples/kea-config-tool/master-db-connect-cql.ini @@ -0,0 +1,26 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "instance-id": "kea-config-tool", + "master-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "", + "type": "cql", + "user": "" + } + }, + "Dhcp6": { + "configuration-type": "database", + "instance-id": "kea-config-tool", + "master-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "", + "type": "cql", + "user": "" + } + } +} diff --git a/doc/examples/kea-config-tool/master-db-connect-mysql.ini b/doc/examples/kea-config-tool/master-db-connect-mysql.ini new file mode 100644 index 0000000000..abe2561275 --- /dev/null +++ b/doc/examples/kea-config-tool/master-db-connect-mysql.ini @@ -0,0 +1,24 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "instance-id": "kea-config-tool", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + }, + "Dhcp6": { + "configuration-type": "database", + "instance-id": "kea-config-tool", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/master-db-connect-pgsql.ini b/doc/examples/kea-config-tool/master-db-connect-pgsql.ini new file mode 100644 index 0000000000..4977a4e59c --- /dev/null +++ b/doc/examples/kea-config-tool/master-db-connect-pgsql.ini @@ -0,0 +1,24 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "instance-id": "kea-config-tool", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + }, + "Dhcp6": { + "configuration-type": "database", + "instance-id": "kea-config-tool", + "master-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea4/backends.json b/doc/examples/kea4/backends.json index dc59289441..9ce41e292e 100644 --- a/doc/examples/kea4/backends.json +++ b/doc/examples/kea4/backends.json @@ -72,7 +72,8 @@ // "type": "cql", // "keyspace": "keatest", // "contact-points": "192.0.2.1,192.0.2.2,192.0.2.3", -// "port": 9042 +// "port": 9042, +// "max-statement-tries": 3 // }, // Addresses will be assigned with a lifetime of 4000 seconds. diff --git a/doc/guide/Makefile.am b/doc/guide/Makefile.am index 1cb54cc6fc..c23dcc4a70 100644 --- a/doc/guide/Makefile.am +++ b/doc/guide/Makefile.am @@ -10,6 +10,7 @@ DOCBOOK += keactrl.xml dhcp4-srv.xml dhcp6-srv.xml lease-expiration.xml logging. DOCBOOK += ddns.xml hooks.xml hooks-ha.xml hooks-host-cache.xml hooks-radius.xml DOCBOOK += hooks-stat-cmds.xml libdhcp.xml lfc.xml stats.xml ctrl-channel.xml DOCBOOK += faq.xml classify.xml shell.xml agent.xml +DOCBOOK += kea-config-tool.xml EXTRA_DIST = $(DOCBOOK) diff --git a/doc/guide/intro.xml b/doc/guide/intro.xml index b857c72845..3ff8c5b189 100644 --- a/doc/guide/intro.xml +++ b/doc/guide/intro.xml @@ -79,7 +79,7 @@ - In order to store lease information in a MySQL database, Kea requires MySQL + In order to store lease information in a MySQL database, Kea requires MySQL headers and libraries. This is an optional dependency in that Kea can be built without MySQL support. @@ -87,7 +87,7 @@ - In order to store lease information in a PostgreSQL database, Kea requires PostgreSQL + In order to store lease information in a PostgreSQL database, Kea requires PostgreSQL headers and libraries. This is an optional dependency in that Kea can be built without PostgreSQL support. @@ -95,7 +95,7 @@ - In order to store lease information in a Cassandra database (CQL), Kea + In order to store lease information in a Cassandra database (CQL), Kea requires Cassandra headers and libraries. This is an optional dependency in that Kea can be built without Cassandra support. @@ -158,12 +158,19 @@ + + + kea-config-tool — + A tool used to reconfigure a Kea server in realtime. + + + kea-lfc — This process removes redundant information from the files used to provide persistent storage for the memfile data base backend. - While it can be run standalone, it is normally run as and when + While it can be run standalone, it is normally run as and when required by the Kea DHCP servers. diff --git a/doc/guide/kea-config-tool.xml b/doc/guide/kea-config-tool.xml new file mode 100644 index 0000000000..adbd4ccda5 --- /dev/null +++ b/doc/guide/kea-config-tool.xml @@ -0,0 +1,280 @@ + +Kea Config Tool + + Kea Configuration Tool is a tool used by system administrators to + initialize and maintain Kea server configuration when the server uses + database backend for leases, reservations and stateless configuration. + + + The tool can be used to manage both DHCPv4 and DHCPv6 server + configurations. + + The executable file's name is kea-config-tool. + + + The dependencies are statically linked so no Kea libraries are + required in order to run it. + +
Usage + + + There are three types of commands supported by the tool: + + -dbinit: + Database initialization + -shard: + Operations with shards databases + -master: + Operations with the master database + + + +
<command>-dbinit</command> command + + + The -dbinit command is a wrapper for + kea-admin. When passed as argument to + kea-config-tool, it executes the kea-admin + and returns it’s exit status. + + It can be used to create and delete databases, intialize tables + that store leases, hosts, reservations or any other usual Kea table. + Also, this supports creation of server configuration tables in shard + databases and master database. +
+ +
<command>-shard</command> command + + + This command is able to write server configuration data into + shards and save server configuration data from shards. + +
Command format + + To write the provided server configuration into shards: + -shard -set-config -4|-6 master-config-file + input-shards-directory-path [list-of-shards] + To retrieve server configuration from shards and writes them + locally: -shard -get-config -4|-6 master-config-file + output-shards-directory-path list-of-shards +
+ +
Command options + + + -4|-6: specifies if the operation + applies either to the DHCPv4 server or to the DHCPv6 server + + master-config-file: + configuration file which provides backend credentials to the + master database. + The following is a DHCPv4 configuration example: + +{ + "Dhcp4": { + "configuration-type": "database", + "master-database": { + "type": "cql", + "keyspace": "kea_master", + "contact-points": "127.0.0.1", + "max-statement-tries": 1, + "name": "keatest", + "user": "keatest", + "password": "keatest", + "port": "9042" + } + } +} + + And a DHCPv6 configuration example: + +{ + "Dhcp6": { + "configuration-type": "database", + "master-database": { + "type": "cql", + "keyspace": "kea_master", + "contact-points": "127.0.0.1", + "max-statement-tries": 1, + "name": "keatest", + "user": "keatest", + "password": "keatest", + "port": "9042" + } + } +} + + + input-shards-directory-path: the path + to the shards configuration directory. The directory must + contain one directory for each shard database. It is mandatory + that the shard directory has the same name as the shard + database. The directory should contain the following shard + configuration files: config.json, + config.generic, config.timestamp + . The config.json file contains + the JSON server configuration data and is required by the Kea + server. The config.generic file may contain + generic data (i.e. YAML or other format) used by system + administrators to generate the JSON configuration data. The + config.timestamp file contains the + timestamp of the database record. If the + config.timestamp file is missing then the timestamp + of the configuration is considered zero. + output-shards-directory-path: the path + where the shards configuration files will be stored. A directory + structure containing shard settings is created. + list-of-shards: comma-separated + shards' names for which the command is applied (for the case + when list-of-shards is optional and no value + is provided then the command is executed for all found shards). + + +
+
+ +
<command>-master</command> command + + + This command is able to write server configuration data into the + master database and save server configuration data from the master + database. + +
Command format + + + To write the provided server configuration into shards: + -master -set-servers -4|-6 master-config-file + input-shards-directory-path [list-of-shards] + To retrieve server configuration locally from shards: + -master -get-servers -4|-6 master-config-file + output-shards-directory-path list-of-shards +
+ +
Command options + + + -4|-6: specifies if the operation + applies either to the DHCPv4 server or to the DHCPv6 server + + master-config-file: configuration + file which provides backend credentials to the master database. + The following is a DHCPv4 configuration example: + +{ + "Dhcp4": { + "configuration-type": "database", + "master-database": { + "type": "cql", + "keyspace": "kea_master", + "contact-points": "127.0.0.1", + "max-statement-tries": 1, + "name": "keatest", + "user": "keatest", + "password": "keatest", + "port": "9042" + } + } +} + + And a DHCPv6 configuration example: + +{ + "Dhcp6": { + "configuration-type": "database", + "master-database": { + "type": "cql", + "keyspace": "kea_master", + "contact-points": "127.0.0.1", + "max-statement-tries": 1, + "name": "keatest", + "user": "keatest", + "password": "keatest", + "port": "9042" + } + } +} + + + input-shards-directory-path: the path + to the shards configuration directory. The directory must + contain one directory for each shard database. It is mandatory + that the shard directory has the same name as the shard + database. The directory should contain the following shard + configuration files: config.json, + config.generic, config.timestamp + . The config.json file contains + the JSON server configuration data and is required by the Kea + server. The config.generic file may contain + generic data (i.e. YAML or other format) used by system + administrators to generate the JSON configuration data. The + config.timestamp file contains the + timestamp of the database record. If the + config.timestamp file is missing then the timestamp + of the configuration is considered zero. + output-shards-directory-path: the path + where the shards configuration files will be stored. A directory + structure containing shard settings is created. + list-of-shards: comma-separated + shards' names for which the command is applied (for the case + when list-of-shards is optional and no value + is provided then the command is executed for all found shards). + + +
+
+
+
Building with DataStax Cassandra C++ Driver + + Package dependencies: + + autoconf + automake + autotools-dev + g++ + libboost-system-dev + liblog4cplus + libtool + libuv0 + libuv-dev + m4 + make + python-support + + + + Get the sources from + https://gitlab.qualitance.com/terastream/dhcp/tree/kea-datastax + into /path/to/kea/sources + . + + Download datastax_scripts.tar. + + Untar datastax_scripts.tar: + tar -fvxz datastax_scripts.tar + + Download the DataStax C++ driver from + https://github.com/datastax/cpp-driver. + + Set the path to the DataStax cpp driver in + datastax_cassandra_config_defines.sh configuration file: + DSC_PATH="/path/to/datastax/cpp/driver" + + + Run build and install commands: + + mkdir /usr/kea + cd /path/to/kea/sources + + autoreconf --install + ./configure --enable-static-link + --enable-static + --with-cql=/path/to/datastax/script/directory/datastax_cassandra_config.sh + --prefix=/usr/kea + make + sudo make install + + +
+ +
diff --git a/doc/guide/lfc.xml b/doc/guide/lfc.xml index e689bde5aa..b90fe696bc 100644 --- a/doc/guide/lfc.xml +++ b/doc/guide/lfc.xml @@ -15,7 +15,7 @@ kea-lfc is a service process that removes redundant information from the files used to provide persistent storage for the memfile data base backend. This service is written to run as a - stand alone process. + stand alone process. While kea-lfc can be started externally, there is usually no need to do this. kea-lfc is run on a periodic diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 67bd58a5c4..2452a26161 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -1,5 +1,5 @@ # The following build order must be maintained. -SUBDIRS = dhcp4 dhcp6 d2 agent perfdhcp admin lfc keactrl +SUBDIRS = dhcp4 dhcp6 d2 agent perfdhcp admin lfc keactrl kea_config_tool if KEA_SHELL SUBDIRS += shell diff --git a/src/bin/admin/admin-utils.sh b/src/bin/admin/admin-utils.sh index 2a9d4dccf8..649c31f779 100755 --- a/src/bin/admin/admin-utils.sh +++ b/src/bin/admin/admin-utils.sh @@ -18,12 +18,34 @@ mysql_execute() { QUERY=$1 shift - if [ $# -gt 1 ]; then - mysql -N -B "$@" -e "${QUERY}" + if [ $# -gt 0 ]; then + cmdline=$(mysql_compose_connect_cmd_line) + cmdline="$cmdline $* -e \"$QUERY\"" + eval "mysql " $cmdline retcode=$? else - mysql -N -B --database="${db_name}" --user="${db_user}" --password="${db_password}" -e "${QUERY}" - retcode=$? + cmdline=$(mysql_compose_connect_cmd_line) + cmdline="$cmdline -e \"$QUERY\" $db_name" + eval "mysql " $cmdline + retcode="$?" + fi + + return $retcode +} + +mysql_execute_no_dbname() { + QUERY=$1 + shift + + cmdline=$(mysql_compose_connect_cmd_line) + cmdline="$cmdline -e \"$QUERY\"" + + eval "mysql " $cmdline + retcode="$?" + + if [ $retcode -ne 0 ]; then + printf "mysql returned with exit status $retcode\n" + exit $retcode fi return $retcode @@ -32,22 +54,80 @@ mysql_execute() { mysql_execute_script() { file=$1 shift - if [ $# -ge 1 ]; then - mysql -N -B "$@" < "${file}" + if [ $# -gt 0 ]; then + cmdline=$(mysql_compose_connect_cmd_line) + eval "mysql " $cmdline $* < ${file} retcode=$? else - mysql -N -B --database="${db_name}" --user="${db_user}" --password="${db_password}" < "${file}" - retcode=$? + cmdline=$(mysql_compose_connect_cmd_line) + eval "mysql "$cmdline $db_name < ${file} + retcode="$?" fi return $retcode } mysql_version() { - mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version" "$@" + mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version;" "$@" return $? } +mysql_config_version() { + VERSION="0.0" + + RESULT=$(mysql_execute "SHOW TABLES;" "$@") + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "mysql_config_version table query failed, mysql status = $ERRCODE" + exit 1 + fi + + COUNT=$(echo $RESULT | grep config_schema_version | wc -w) + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "mysql_config_version table query failed, mysql status = $ERRCODE" + exit 1 + fi + + if [ $COUNT -gt 0 ]; then + VERSION=$(mysql_execute "SELECT CONCAT(version,'.',minor) FROM config_schema_version;" "$@") + fi + ERRCODE=$? + echo $VERSION + + return $ERRCODE +} + +mysql_master_version() { + VERSION="0.0" + + RESULT=`mysql_execute "SHOW TABLES;" "$@"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "mysql_master_version table query failed, mysql status = $ERRCODE" + exit 1 + fi + + COUNT=`echo $RESULT | grep master_schema_version | wc -w` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "mysql_master_version table query failed, mysql status = $ERRCODE" + exit 1 + fi + + if [ $COUNT -gt 0 ]; then + VERSION=$(mysql_execute "SELECT CONCAT(version,'.',minor) FROM master_schema_version;" "$@") + fi + ERRCODE=$? + echo $VERSION + + return $ERRCODE +} + # Submits given SQL text to PostgreSQL # There are two ways of calling this method. # pgsql_execute SQL_QUERY - This call is simpler, but requires db_user, @@ -62,16 +142,38 @@ pgsql_execute() { QUERY=$1 shift if [ $# -gt 0 ]; then - echo "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q "$@" + export PGPASSWORD=$db_password + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline $*" + echo $QUERY | psql $cmdline retcode=$? else export PGPASSWORD=$db_password - echo "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -U "${db_user}" -d "${db_name}" + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline -d $db_name" + echo $QUERY | psql $cmdline retcode=$? fi return $retcode } +pgsql_execute_no_dbname() { + QUERY=$1 + shift + export PGPASSWORD=$db_password + cmdline=$(pgsql_compose_connect_cmd_line) +# Sometimes we don't need to connect to a specific database +# (e.g. when we want to create a new one) +# Postgresql client requires to connect all the time a database. +# The first database is always created by the initdb command when +# the data storage area is initialized is called postgres. +# So we will connect to the default database "postgres") + cmdline="$cmdline -d postgres" + echo $QUERY | psql $cmdline + retcode=$? + return $retcode +} + # Submits SQL in a given file to PostgreSQL # There are two ways of calling this method. # pgsql_execute SQL_FILE - This call is simpler, but requires db_user, @@ -86,11 +188,16 @@ pgsql_execute_script() { file=$1 shift if [ $# -gt 0 ]; then - psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -f "${file}" "$@" + export PGPASSWORD=$db_password + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline -d $db_name -f $file $*" + eval "psql "$cmdline retcode=$? else export PGPASSWORD=$db_password - psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -U "${db_user}" -d "${db_name}" -f "${file}" + cmdline=$(pgsql_compose_connect_cmd_line) + cmdline="$cmdline -d $db_name -f $file" + eval "psql "$cmdline retcode=$? fi return $retcode @@ -101,14 +208,74 @@ pgsql_version() { return $? } +pgsql_config_version() { + VERSION="0.0" + + RESULT=`pgsql_execute "\d" "$@"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "pgsql_config_version table query failed, pgsql status = $ERRCODE" + exit 1 + fi + + COUNT=`echo $RESULT | grep config_schema_version | wc -w` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "pgsql_config_version table query failed, pgsql status = $ERRCODE" + exit 1 + fi + + if [ $COUNT -gt 0 ]; then + VERSION=`pgsql_execute "SELECT version || '.' || minor FROM config_schema_version" "$@"` + fi + ERRCODE=$? + echo $VERSION + + return $ERRCODE +} + +pgsql_master_version() { + VERSION="0.0" + + RESULT=`pgsql_execute "\d" "$@"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "pgsql_master_version table query failed, pgsql status = $ERRCODE" + exit 1 + fi + + COUNT=`echo $RESULT | grep master_schema_version | wc -w` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + printf "pgsql_master_version table query failed, pgsql status = $ERRCODE" + exit 1 + fi + + if [ $COUNT -gt 0 ]; then + VERSION=`pgsql_execute "SELECT version || '.' || minor FROM master_schema_version" "$@"` + fi + ERRCODE=$? + echo $VERSION + + return $ERRCODE +} + cql_execute() { query=$1 shift - if [ $# -gt 1 ]; then - cqlsh "$@" -e "$query" + if [ $# -gt 0 ]; then + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline $* -e \"$query\"" + eval "cqlsh " $cmdline retcode=$? else - cqlsh -u "${db_user}" -p "${db_password}" -k "${db_name}" -e "${query}" + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline -k $db_name -e \"$query\"" + eval "cqlsh " $cmdline retcode=$? fi @@ -120,14 +287,35 @@ cql_execute() { return $retcode } +cql_execute_no_keyspace() { + query=$1 + shift + + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline -e \"$query\"" + eval "cqlsh " $cmdline + retcode=$? + + if [ $retcode -ne 0 ]; then + printf "cqlsh returned with exit status $retcode\n" + exit $retcode + fi + + return $retcode +} + cql_execute_script() { file=$1 shift - if [ $# -gt 1 ]; then - cqlsh "$@" -e "$file" + if [ $# -gt 0 ]; then + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline $* -f \"$file\"" + eval "cqlsh " $cmdline retcode=$? else - cqlsh -u "${db_user}" -p "${db_password}" -k "${db_name}" -f "${file}" + cmdline=$(cql_compose_connect_cmd_line) + cmdline="$cmdline -k $db_name -f \"$file\"" + eval "cqlsh " $cmdline retcode=$? fi @@ -143,6 +331,117 @@ cql_version() { version=$(cql_execute "SELECT version, minor FROM schema_version" "$@") error=$? version=$(echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 | tr "|" ".") - echo "$version" + echo $version + return $error +} + +cql_config_version() { + version="0.0" + result=$(cql_execute "DESCRIBE tables;" "$@") + count=$(echo $result | grep config_schema_version | wc -w) + if [ $count -gt 0 ]; then + version=$(cql_execute "SELECT version, minor FROM config_schema_version" "$@") + version=$(echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 --output-delimiter=".") + fi + error=$? + echo $version return $error } + +cql_master_version() { + version="0.0" + result=$(cql_execute "DESCRIBE tables;" "$@") + count=$(echo $result | grep master_schema_version | wc -w) + if [ $count -gt 0 ]; then + version=$(cql_execute "SELECT version, minor FROM master_schema_version" "$@") + version=$(echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 --output-delimiter=".") + fi + error=$? + echo $version + return $error +} + +mysql_compose_connect_cmd_line() { + local line="-N -B" + + if [ -n "$db_server_address" ]; then + line="$line --host=$db_server_address" + fi + + if [ -n "$db_server_port" ]; then + line="$line --port=$db_server_port" + fi + + if [ -n "$db_user" ]; then + line="$line --user=$db_user" + fi + + if [ -n "$db_password" ]; then + line="$line --password=$db_password" + fi + + if [ -n "$db_host" ]; then + line="$line --host=$db_host" + fi + + echo $line +} + +pgsql_compose_connect_cmd_line() { + local line="--set ON_ERROR_STOP=1 -A -t -q" + + if [ -n "$db_server_address" ] + then + line="$line -h $db_server_address" + else + if [ -n "$db_host" ]; then + line="$line -h $db_host" + else + line="$line -h localhost" + fi + fi + + if [ -n "$db_server_port" ]; then + line="$line -p $db_server_port" + fi + + if [ -n "$db_user" ]; then + line="$line -U $db_user" + fi + + if [ -n "$db_host" ]; then + line="$line -h $db_host" + fi + + echo $line +} + +cql_compose_connect_cmd_line() { + local line="" + + if [ -n "$db_server_address" ]; then + line=$line" "$db_server_address + fi + + if [ -n "$db_server_port" ]; then + line=$line" "$db_server_port + fi + + if [ -n "$db_server_version" ]; then + line=$line" --cqlversion="$db_server_version + fi + + if [ -n "$db_user" ]; then + line=$line" -u "$db_user + fi + + if [ -n "$db_password" ]; then + line=$line" -p "$db_password + fi + + if [ -n "$db_use_ssl" ]; then + line=$line" --ssl" + fi + + echo $line +} diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 4be383ddb4..0687b401cd 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -25,6 +25,11 @@ db_host="localhost" db_user="keatest" db_password="keatest" db_name="keatest" +db_server_address="" +db_server_port="" + +# These are CQL default parameters +db_server_version="" # lease dump parameters dump_type=0 @@ -50,16 +55,32 @@ usage() { printf "\n" printf "COMMAND: Currently supported operations are:\n" printf "\n" + printf " - -h or --help: Displays this help.\n" + printf " - db-create: Creates new empty databases. Useful for first time installation.\n" + printf " - db-remove: Removes an existing database.\n" + printf " - create-db-and-users: internal use only\n" printf " - lease-init: Initializes new lease database. Useful for first time installation.\n" + printf " - lease-drop: Drops all tables in the lease database. \n" printf " - lease-version: Checks version of the existing lease database scheme. Useful\n" printf " - for checking lease DB version when preparing for an upgrade.\n" printf " - lease-upgrade: Upgrades your lease database scheme\n" printf " - lease-dump: Dump current leases to a CSV file\n" + printf " - config-init: Initalizes new config database. Useful for first time installation.\n" + printf " - config-version: Checks version of the existing config database scheme. Useful\n" + printf " - for checking config DB version when preparing for an upgrade.\n" + printf " - config-upgrade: Upgrades your config database scheme\n" + printf " - master-init: Initalizes new master config database. Useful for first time installation.\n" + printf " - master-version: Checks version of the existing master config database scheme. Useful\n" + printf " - for checking master config DB version when preparing for an upgrade.\n" + printf " - master-upgrade: Upgrades your master config database scheme\n" printf "\n" printf "BACKEND - one of the supported backends: memfile|mysql|pgsql|cql\n" printf "\n" printf "PARAMETERS: Parameters are optional in general, but may be required\n" printf " for specific operation.\n" + printf " -s or --server - specifies remote database server address\n" + printf " -sp or --server-port - specifies remote database server port\n" + printf " --db-server-version - specifies remote database version\n" printf " -h or --host hostname - specifies a hostname of a database to connect to\n" printf " -u or --user name - specifies username when connecting to a database\n" printf " -p or --password pass - specifies a password when connecting to a database\n" @@ -125,12 +146,24 @@ memfile_init() { exit 1 } +memfile_config_init() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + +memfile_master_init() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + # Initializes a new, empty MySQL database. # It essentially calls scripts/mysql/dhcpdb_create.mysql script, with # some extra sanity checks. It will refuse to use it if there are any # existing tables. It's better safe than sorry. mysql_init() { - printf "Checking if there is a database initialized already. Please ignore errors.\n" + printf "Checking if there is a database initialized already.\n" # Let's try to count the number of tables. Anything above 0 means that there # is some database in place. If there is anything, we abort. Note that @@ -140,7 +173,7 @@ mysql_init() { # RESULT=`mysql_execute "SHOW TABLES;"` ERRCODE=$? - if [ $ERRCODE -ne 0 ] + if [ $ERRCODE -ne 0 ] then log_error "mysql_init table query failed, mysql status = $ERRCODE" exit 1 @@ -155,7 +188,7 @@ mysql_init() { fi printf "Initializing database using script %s\n" $scripts_dir/mysql/dhcpdb_create.mysql - mysql -B --host=$db_host --user=$db_user --password=$db_password $db_name < $scripts_dir/mysql/dhcpdb_create.mysql + mysql_execute_script $scripts_dir/mysql/dhcpdb_create.mysql ERRCODE=$? printf "mysql returned status code $ERRCODE\n" @@ -169,8 +202,107 @@ mysql_init() { exit $ERRCODE } +#Creates a new, empty MySQL database. +mysql_db_create() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Creating '$name' database\n" + + command="CREATE DATABASE $name;" + RESULT=`mysql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database creation failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been created.\n" + done + exit 0 +} + +#Removes an existing MySQL database. +mysql_db_remove() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Removing '$name' database\n" + + command="DROP DATABASE $name;" + RESULT=`mysql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database deletion failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been removed.\n" + done + exit 0 +} + +mysql_config_init() { + mysql_execute_script $scripts_dir/mysql/configdb_create.mysql + + version=`mysql_config_version` + printf "Config DB version reported after initialization: $version\n" + + exit 0 +} + +mysql_master_init() { + + mysql_execute_script $scripts_dir/mysql/masterdb_create.mysql + + version=`mysql_master_version` + printf "Config DB version reported after initialization: $version\n" + + exit 0 +} + +#Creates a new, empty Postgresql database. +pgsql_db_create() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Creating '$name' database\n" + + command="CREATE DATABASE $name;" + RESULT=`pgsql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database creation failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been created.\n" + done + exit 0 +} + +#Removes an existing MySQL database. +pgsql_db_remove() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Removing '$name' database\n" + + command="DROP DATABASE $name;" + RESULT=`pgsql_execute_no_dbname "$command"` + ERRCODE=$? + if [ $ERRCODE -ne 0 ] + then + log_error "Database deletion failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The database has been removed.\n" + done + exit 0 +} + pgsql_init() { - printf "Checking if there is a database initialized already. Please ignore errors.\n" + printf "Checking if there is a database initialized already.\n" # Let's try to count the number of tables. Anything above 0 means that there # is some database in place. If there is anything, we abort. @@ -202,24 +334,152 @@ pgsql_init() { exit 0 } +pgsql_config_init() { + pgsql_execute_script $scripts_dir/pgsql/configdb_create.pgsql + + version=`pgsql_config_version` + printf "Config DB version reported after initialization: $version\n" + + exit 0 +} + +pgsql_master_init() { + + pgsql_execute_script $scripts_dir/pgsql/masterdb_create.pgsql + + version=`pgsql_master_version` + printf "Config DB version reported after initialization: $version\n" + + exit 0 +} + +# Creates a new, empty CQL database. +cql_db_create() { + #setting default values for the keyspace strategy and replication + strategy="SimpleStrategy" + replication="1" + + #loading the keyspace strategy and replication from file + . "$scripts_dir/cql/masterdb_create_config.sh" + ERRCODE=$? + if [ "$ERRCODE" -ne 0 ]; then + log_error "Could not execute script 'masterdb_create_config.sh' file, status code: $ERRCODE?" + exit 1 + fi + + for name in $(echo $db_name | tr "," "\n") + do + printf "Creating '$name' keyspace\n" + + command="create KEYSPACE $name WITH REPLICATION = { 'class' : '$strategy', 'replication_factor' : $replication };" + RESULT=`cql_execute_no_keyspace "$command"` + ERRCODE=$? + if [ "$ERRCODE" -ne 0 ]; then + log_error "Keyspace creation failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The keyspace has been created.\n" + done + + exit 0 +} + +# Removes an existing CQL database. +cql_db_remove() { + for name in $(echo $db_name | tr "," "\n") + do + printf "Removing '$name' keyspace\n" + + command="drop KEYSPACE $name" + RESULT=`cql_execute_no_keyspace "$command"` + ERRCODE=$? + if [ "$ERRCODE" -ne 0 ]; then + log_error "Keyspace deletion failed, status code: $ERRCODE?" + exit 1 + fi + + printf "The keyspace has been removed.\n" + done + + exit 0 +} + cql_init() { - printf "Checking if there is a database initialized already... Please ignore errors.\n" + printf "Checking if there is a database initialized already...\n" result=$(cql_execute "DESCRIBE tables;") + cql_exit_code=${?} if [ $(echo "$result" | grep "" | wc -w) -gt 0 ]; then printf "Creating and initializing tables using script %s...\n" $scripts_dir/cql/dhcpdb_create.cql cql_execute_script $scripts_dir/cql/dhcpdb_create.cql else - log_error "Expected empty database $db_name, but the following tables are present \n$result. Aborting." + if [ ${cql_exit_code} -eq 0 ]; then + log_error "ERROR: Expected empty database $db_name, but the following tables are present: \n$result. Aborting." + else + log_error "ERROR: Unable to execute cql_execute, reason: \n$result. Aborting." + fi exit 2 fi version=$(cql_version) - printf "Lease DB version reported after initialization: %s\n" "$version" + printf "Lease DB version reported after initialization: %s\n" "${version}" + + exit 0 +} + +cql_config_init() { + cql_execute_script $scripts_dir/cql/configdb_create.cql + + version=`cql_config_version` + printf "Config DB version reported after initialization: $version\n" + + exit 0 +} + +cql_master_init() { + cql_execute_script $scripts_dir/cql/masterdb_create.cql + + version=`cql_master_version` + printf "Config DB version reported after initialization: $version\n" exit 0 } +# Drop functions +memfile_drop() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + +mysql_drop() { + script=$scripts_dir/mysql/dhcpdb_drop.mysql + printf "Droping tables in database using script $script%s\n" + mysql -B --user=$db_user --password=$db_password $db_name < $script + return_code=$? + printf "mysql_drop returned with code $return_code\n" + exit $return_code +} + +pgsql_drop() { + script="$scripts_dir/pgsql/dhcpdb_drop.pgsql" + printf "Droping tables in database using script $script%s\n" + pgsql_execute_script $script + return_code=$? + printf "pgsql_drop returned with code $return_code\n" + exit $return_code +} + +cql_drop() { + script="$scripts_dir/cql/dhcpdb_drop.cql" + printf "Droping tables in database using script $script%s\n" + cql_execute_script $script + return_code=$? + printf "cql_drop returned with code $return_code\n" + exit $return_code +} + ### Functions that implement database version checking commands memfile_version() { # @todo Implement this as part of #3601 @@ -227,6 +487,18 @@ memfile_version() { exit 1 } +memfile_config_version() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + +memfile_master_version() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + ### Functions used for upgrade memfile_upgrade() { # @todo Implement this as part of #3601 @@ -234,6 +506,20 @@ memfile_upgrade() { exit 1 } +### Functions used for upgrade +memfile_config_upgrade() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + +### Functions used for upgrade +memfile_master_upgrade() { + # @todo Implement this as part of #3601 + log_error "NOT IMPLEMENTED" + exit 1 +} + # Upgrades existing MySQL database installation. The idea is that # it will go over all upgrade scripts from (prefix)/share/kea/scripts/mysql # and run them one by one. They will be named properly, so they will @@ -270,6 +556,66 @@ mysql_upgrade() { printf "\n" } +mysql_config_upgrade() { + + printf "Config DB version reported before upgrade: " + mysql_config_version + printf "\n" + + # Check if the scripts directory exists at all. + if [ ! -d ${scripts_dir}/mysql ]; then + log_error "Invalid scripts directory: ${scripts_dir}/mysql" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find ${scripts_dir}/mysql/config_upgrade*.sh -type f | wc -l) + if [ $num_files -eq 0 ]; then + log_error "No scripts in ${scripts_dir}/mysql or the directory is not readable or does not have any config_upgrade* scripts." + exit 1 + fi + + for script in ${scripts_dir}/mysql/config_upgrade*.sh + do + echo "Processing $script file..." + sh ${script} --user=${db_user} --password=${db_password} ${db_name} + done + + printf "Config DB version reported after upgrade: " + mysql_config_version + printf "\n" +} + +mysql_master_upgrade() { + + printf "Config DB version reported before upgrade: " + mysql_master_version + printf "\n" + + # Check if the scripts directory exists at all. + if [ ! -d ${scripts_dir}/mysql ]; then + log_error "Invalid scripts directory: ${scripts_dir}/mysql" + exit 1 + fi + + Check if there are any files in it + num_files=$(find ${scripts_dir}/mysql/master_upgrade*.sh -type f | wc -l) + if [ $num_files -eq 0 ]; then + log_error "No scripts in ${scripts_dir}/mysql or the directory is not readable or does not have any config_upgrade* scripts." + exit 1 + fi + + for script in ${scripts_dir}/mysql/master_upgrade*.sh + do + echo "Processing $script file..." + sh ${script} --user=${db_user} --password=${db_password} ${db_name} + done + + printf "Config DB version reported after upgrade: " + mysql_master_version + printf "\n" +} + pgsql_upgrade() { version=`pgsql_version` printf "Lease DB version reported before upgrade: $version\n" @@ -302,6 +648,70 @@ pgsql_upgrade() { exit 0 } +pgsql_config_upgrade() { + version=`pgsql_config_version` + printf "Config DB version reported before upgrade: $version\n" + + # Check if the scripts directory exists at all. + if [ ! -d ${scripts_dir}/pgsql ]; then + log_error "Invalid scripts directory: ${scripts_dir}/pgsql" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find ${scripts_dir}/pgsql/config_upgrade*.sh -type f | wc -l) + if [ $num_files -eq 0 ]; then + log_error "No scripts in ${scripts_dir}/pgsql or the directory is not readable or does not have any config_upgrade* scripts." + exit 1 + fi + + # Postgres psql does not accept pw on command line, but can do it + # thru an env + export PGPASSWORD=$db_password + + for script in ${scripts_dir}/pgsql/config_upgrade*.sh + do + echo "Processing $script file..." + sh ${script} -U ${db_user} -h localhost -d ${db_name} + done + + version=`pgsql_config_version` + printf "Config DB version reported after upgrade: $version\n" + exit 0 +} + +pgsql_master_upgrade() { + version=`pgsql_master_version` + printf "Config DB version reported before upgrade: $version\n" + + # Check if the scripts directory exists at all. + if [ ! -d ${scripts_dir}/pgsql ]; then + log_error "Invalid scripts directory: ${scripts_dir}/pgsql" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find ${scripts_dir}/pgsql/master_upgrade*.sh -type f | wc -l) + if [ $num_files -eq 0 ]; then + log_error "No scripts in ${scripts_dir}/pgsql or the directory is not readable or does not have any config_upgrade* scripts." + exit 1 + fi + + # Postgres psql does not accept pw on command line, but can do it + # thru an env + export PGPASSWORD=$db_password + + for script in ${scripts_dir}/pgsql/master_upgrade*.sh + do + echo "Processing $script file..." + sh ${script} -U ${db_user} -h localhost -d ${db_name} + done + + version=`pgsql_master_version` + printf "Config DB version reported after upgrade: $version\n" + exit 0 +} + cql_upgrade() { version=`cql_version` printf "Lease DB version reported before upgrade: $version\n" @@ -324,7 +734,11 @@ cql_upgrade() { for script in ${scripts_dir}/cql/upgrade*.sh do echo "Processing $script file..." - sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} + if [ -z ${db_server_version} ]; then + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} + else + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} --cqlversion=${db_server_version} + fi done else echo "No upgrade script available." @@ -335,6 +749,80 @@ cql_upgrade() { exit 0 } +cql_config_upgrade() { + version=`cql_config_version` + printf "Config DB version reported before upgrade: $version\n" + + # Check if the scripts directory exists at all. + if [ ! -d ${scripts_dir}/cql ]; then + log_error "Invalid scripts directory: ${scripts_dir}/cql" + exit 1 + fi + + # Check if directory is readable. + if [ ! -r ${scripts_dir}/cql ]; then + log_error "Directory is not readable: ${scripts_dir}/cql" + exit 1 + fi + + # Check if there are upgrade scripts. + files=$(find ${scripts_dir}/cql/config_upgrade*.sh -type f) + if [ $? -eq 0 ]; then # Upgrade scripts are present. + for script in ${scripts_dir}/cql/config_upgrade*.sh + do + echo "Processing $script file..." + if [ -z ${db_server_version} ]; then + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} + else + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} --cqlversion=${db_server_version} + fi + done + else + echo "No upgrade script available." + fi + + version=`cql_config_version` + printf "Config DB version reported after upgrade: $version\n" + exit 0 +} + +cql_master_upgrade() { + version=`cql_master_version` + printf "Config DB version reported before upgrade: $version\n" + + # Check if the scripts directory exists at all. + if [ ! -d ${scripts_dir}/cql ]; then + log_error "Invalid scripts directory: ${scripts_dir}/cql" + exit 1 + fi + + # Check if directory is readable. + if [ ! -r ${scripts_dir}/cql ]; then + log_error "Directory is not readable: ${scripts_dir}/cql" + exit 1 + fi + + # Check if there are upgrade scripts. + files=$(find ${scripts_dir}/cql/master_upgrade*.sh -type f) + if [ $? -eq 0 ]; then # Upgrade scripts are present. + for script in ${scripts_dir}/cql/master_upgrade*.sh + do + echo "Processing $script file..." + if [ -z ${db_server_version} ]; then + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} + else + sh ${script} -u ${db_user} -p ${db_password} -k ${db_name} --cqlversion=${db_server_version} + fi + done + else + echo "No upgrade script available." + fi + + version=`cql_master_version` + printf "Config DB version reported after upgrade: $version\n" + exit 0 +} + # Utility function which tests if the given file exists and # if so notifies the user and provides them the opportunity # to abort the current command. @@ -528,6 +1016,57 @@ cql_dump() { exit 0 } + +memfile_create_database_and_users() { + log_error "NOT IMPLEMENTED" + exit 1 +} + +mysql_create_database_and_users() { + mysql_execute "GRANT ALL ON *.* TO keatest IDENTIFIED BY 'keatest';" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} + mysql_execute "CREATE DATABASE keatest;" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} + mysql_execute "GRANT SELECT ON keatest.* TO keatest_readonly IDENTIFIED BY 'keatest';" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} + exit 0 +} + +pgsql_create_database_and_users() { + export PGPASSWORD=${db_password} + query="DO +\$body\$ +BEGIN + IF NOT EXISTS ( + SELECT usename + FROM pg_catalog.pg_user + WHERE usename = 'keatest') THEN + CREATE ROLE keatest LOGIN PASSWORD 'keatest'; + ALTER USER keatest CREATEDB; + END IF; +END +\$body\$;" + pgsql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --username=postgres + pgsql_execute "CREATE DATABASE keatest;" --host=${db_server_address} --port=${db_server_port} --username=postgres + pgsql_execute "GRANT ALL PRIVILEGES ON DATABASE keatest TO keatest;" --host=${db_server_address} --port=${db_server_port} --username=postgres + query="DO +\$body\$ +BEGIN + IF NOT EXISTS ( + SELECT usename + FROM pg_catalog.pg_user + WHERE usename = 'keatest_readonly') THEN + CREATE ROLE keatest_readonly LOGIN PASSWORD 'keatest'; + END IF; +END +\$body\$;" + pgsql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --username=postgres + pgsql_execute "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES to keatest_readonly;" --host=${db_server_address} --port=${db_server_port} --username=postgres + exit 0 +} + +cql_create_database_and_users() { + cql_execute_no_keyspace "CREATE KEYSPACE keatest WITH replication = {'class' : 'SimpleStrategy','replication_factor' : 1}" + exit 0 +} + ### Script starts here ### # First, find what the command is @@ -537,7 +1076,11 @@ if [ -z ${command} ]; then usage exit 1 fi -is_in_list "${command}" "lease-init lease-version lease-upgrade lease-dump" +if [ "${command}" = "-h" -o "${command}" = "--help" ]; then + usage + exit 0 +fi +is_in_list "${command}" "db-create db-remove lease-init lease-drop lease-version lease-upgrade lease-dump config-init config-version config-upgrade master-init master-version master-upgrade create-db-and-users" if [ ${_inlist} -eq 0 ]; then log_error "invalid command: ${command}" exit 1 @@ -563,6 +1106,36 @@ while [ ! -z "${1}" ] do option=${1} case ${option} in + # Specify server address + -s|--server) + shift + db_server_address=${1} + if [ -z ${db_server_address} ]; then + log_error "-s or --server requires a parameter" + usage + exit 1 + fi + ;; + # Specify server port + -sp|--server-port) + shift + db_server_port=${1} + if [ -z ${db_server_port} ]; then + log_error "-sp or --server-port requires a parameter" + usage + exit 1 + fi + ;; + # Specify CQL database version + --db-server-version) + shift + db_server_version=${1} + if [ -z ${db_server_version} ]; then + log_error "--db-server-version requires a parameter" + usage + exit 1 + fi + ;; # Specify database host -h|--host) shift @@ -649,6 +1222,40 @@ do done case ${command} in + # Create the database + db-create) + case ${backend} in + memfile) + log_error "this command is not compatible with memory file backend" + ;; + mysql) + mysql_db_create + ;; + pgsql) + pgsql_db_create + ;; + cql) + cql_db_create + ;; + esac + ;; + # Removes the database + db-remove) + case ${backend} in + memfile) + log_error "this command is not compatible with memory file backend" + ;; + mysql) + mysql_db_remove + ;; + pgsql) + pgsql_db_remove + ;; + cql) + cql_db_remove + ;; + esac + ;; # Initialize the database lease-init) case ${backend} in @@ -666,6 +1273,22 @@ case ${command} in ;; esac ;; + lease-drop) + case ${backend} in + memfile) + memfile_drop + ;; + mysql) + mysql_drop + ;; + pgsql) + pgsql_drop + ;; + cql) + cql_drop + ;; + esac + ;; lease-version) case ${backend} in memfile) @@ -673,7 +1296,6 @@ case ${command} in ;; mysql) mysql_version - printf "\n" ;; pgsql) pgsql_version @@ -715,6 +1337,118 @@ case ${command} in ;; esac ;; + config-init) + case ${backend} in + memfile) + memfile_config_init + ;; + mysql) + mysql_config_init + ;; + pgsql) + pgsql_config_init + ;; + cql) + cql_config_init + ;; + esac + ;; + config-version) + case ${backend} in + memfile) + memfile_config_version + ;; + mysql) + mysql_config_version + ;; + pgsql) + pgsql_config_version + ;; + cql) + cql_config_version + ;; + esac + ;; + config-upgrade) + case ${backend} in + memfile) + memfile_config_upgrade + ;; + mysql) + mysql_config_upgrade + ;; + pgsql) + pgsql_config_upgrade + ;; + cql) + cql_config_upgrade + ;; + esac + ;; + master-init) + case ${backend} in + memfile) + memfile_master_init + ;; + mysql) + mysql_master_init + ;; + pgsql) + pgsql_master_init + ;; + cql) + cql_master_init + ;; + esac + ;; + master-version) + case ${backend} in + memfile) + memfile_master_version + ;; + mysql) + mysql_master_version + ;; + pgsql) + pgsql_master_version + ;; + cql) + cql_master_version + ;; + esac + ;; + master-upgrade) + case ${backend} in + memfile) + memfile_master_upgrade + ;; + mysql) + mysql_master_upgrade + ;; + pgsql) + pgsql_master_upgrade + ;; + cql) + cql_master_upgrade + ;; + esac + ;; + create-db-and-users) + case ${backend} in + memfile) + memfile_create_database_and_users + ;; + mysql) + mysql_create_database_and_users + ;; + pgsql) + pgsql_create_database_and_users + ;; + cql) + cql_create_database_and_users + ;; + esac + ;; esac exit 0 diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.cql b/src/bin/admin/tests/dhcpdb_create_1.0.cql index 577f2ae277..bf08101df6 100644 --- a/src/bin/admin/tests/dhcpdb_create_1.0.cql +++ b/src/bin/admin/tests/dhcpdb_create_1.0.cql @@ -1,4 +1,4 @@ --- Copyright (C) 2015-2017 Deutsche Telekom AG. +-- Copyright (C) 2015-2018 Deutsche Telekom AG. -- Author: Razvan Becheriu @@ -133,6 +133,8 @@ CREATE TABLE IF NOT EXISTS lease_hwaddr_source ( PRIMARY KEY ((hwaddr_source)) ); +INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (0, 'HWADDR_SOURCE_UNKNOWN'); + -- Hardware address obtained from raw sockets INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (1, 'HWADDR_SOURCE_RAW'); @@ -154,6 +156,8 @@ INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE -- Hardware address extracted from docsis options INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS'); +INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (128, 'HWADDR_SOURCE_DOCSIS_MODEM'); + -- Create table holding mapping of the lease states to their names. -- This is not used in queries from the DHCP server but rather in -- direct queries from the lease database management tools. diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.mysql b/src/bin/admin/tests/dhcpdb_create_1.0.mysql index b4823afbff..9f68439ffe 100644 --- a/src/bin/admin/tests/dhcpdb_create_1.0.mysql +++ b/src/bin/admin/tests/dhcpdb_create_1.0.mysql @@ -123,7 +123,7 @@ COMMIT; # # Portability # =========== -# The "ENGINE = INNODB" on some tables is not portablea to another database +# The "ENGINE = INNODB" on some tables is not portable to another database # and will need to be removed. # # Some columns contain binary data so are stored as VARBINARY instead of diff --git a/src/bin/agent/agent_lexer.cc b/src/bin/agent/agent_lexer.cc index 3a386d84a7..3a85021cc6 100644 --- a/src/bin/agent/agent_lexer.cc +++ b/src/bin/agent/agent_lexer.cc @@ -1412,7 +1412,7 @@ unsigned int comment_start_line = 0; using namespace isc; using isc::agent::AgentParser; -}; +} // namespace /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::agent::ParserContext::fatal(msg) diff --git a/src/bin/agent/agent_lexer.ll b/src/bin/agent/agent_lexer.ll index aa1f92c825..8dbc05805d 100644 --- a/src/bin/agent/agent_lexer.ll +++ b/src/bin/agent/agent_lexer.ll @@ -38,7 +38,7 @@ unsigned int comment_start_line = 0; using namespace isc; using isc::agent::AgentParser; -}; +} // namespace /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::agent::ParserContext::fatal(msg) diff --git a/src/bin/agent/ca_command_mgr.cc b/src/bin/agent/ca_command_mgr.cc index 85e998db61..db8231c573 100644 --- a/src/bin/agent/ca_command_mgr.cc +++ b/src/bin/agent/ca_command_mgr.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/agent/ca_log.cc b/src/bin/agent/ca_log.cc index 7c9147c8b3..6564a07735 100644 --- a/src/bin/agent/ca_log.cc +++ b/src/bin/agent/ca_log.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include namespace isc { diff --git a/src/bin/agent/ca_response_creator.cc b/src/bin/agent/ca_response_creator.cc index 5eb85170a9..a6ccb525e6 100644 --- a/src/bin/agent/ca_response_creator.cc +++ b/src/bin/agent/ca_response_creator.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/agent/parser_context.cc b/src/bin/agent/parser_context.cc index 946ce71e17..2efcd5808b 100644 --- a/src/bin/agent/parser_context.cc +++ b/src/bin/agent/parser_context.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/agent/simple_parser.cc b/src/bin/agent/simple_parser.cc index 39d182f829..807b7c7d52 100644 --- a/src/bin/agent/simple_parser.cc +++ b/src/bin/agent/simple_parser.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/agent/tests/ca_unittests.cc b/src/bin/agent/tests/ca_unittests.cc index 44132a47a3..7b4ad44316 100644 --- a/src/bin/agent/tests/ca_unittests.cc +++ b/src/bin/agent/tests/ca_unittests.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/bin/agent/tests/parser_unittests.cc b/src/bin/agent/tests/parser_unittests.cc index c7d8fb7c3b..421c4e3e57 100644 --- a/src/bin/agent/tests/parser_unittests.cc +++ b/src/bin/agent/tests/parser_unittests.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/d2/d2_lexer.cc b/src/bin/d2/d2_lexer.cc index 915f94cab7..fca5683052 100644 --- a/src/bin/d2/d2_lexer.cc +++ b/src/bin/d2/d2_lexer.cc @@ -1124,7 +1124,7 @@ bool start_token_flag = false; isc::d2::D2ParserContext::ParserType start_token_value; unsigned int comment_start_line = 0; -}; +} // namespace /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::d2::D2ParserContext::fatal(msg) diff --git a/src/bin/d2/d2_lexer.ll b/src/bin/d2/d2_lexer.ll index bc26b334bd..8bc8eacbd8 100644 --- a/src/bin/d2/d2_lexer.ll +++ b/src/bin/d2/d2_lexer.ll @@ -34,7 +34,7 @@ bool start_token_flag = false; isc::d2::D2ParserContext::ParserType start_token_value; unsigned int comment_start_line = 0; -}; +} // namespace /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::d2::D2ParserContext::fatal(msg) diff --git a/src/bin/d2/d2_simple_parser.cc b/src/bin/d2/d2_simple_parser.cc index 8e1d0469d0..7c1453303c 100644 --- a/src/bin/d2/d2_simple_parser.cc +++ b/src/bin/d2/d2_simple_parser.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/d2/parser_context.cc b/src/bin/d2/parser_context.cc index 54a136bd6e..e38d0c47b9 100644 --- a/src/bin/d2/parser_context.cc +++ b/src/bin/d2/parser_context.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/d2/tests/parser_unittest.cc b/src/bin/d2/tests/parser_unittest.cc index 9ecd663451..2bb62543f6 100644 --- a/src/bin/d2/tests/parser_unittest.cc +++ b/src/bin/d2/tests/parser_unittest.cc @@ -3,6 +3,8 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am index cc19d70882..ae967a50ca 100644 --- a/src/bin/dhcp4/Makefile.am +++ b/src/bin/dhcp4/Makefile.am @@ -31,7 +31,7 @@ EXTRA_DIST += dhcp4_parser.yy if GENERATE_DOCS kea-dhcp4.8: kea-dhcp4.xml @XSLTPROC@ --novalid --xinclude --nonet -o $@ \ - http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl \ + http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl \ $(srcdir)/kea-dhcp4.xml else @@ -64,6 +64,8 @@ libdhcp4_la_SOURCES += dhcp4_lexer.ll location.hh position.hh stack.hh libdhcp4_la_SOURCES += dhcp4_parser.cc dhcp4_parser.h libdhcp4_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h +libdhcp4_la_LIBADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la + nodist_libdhcp4_la_SOURCES = dhcp4_messages.h dhcp4_messages.cc EXTRA_DIST += dhcp4_messages.mes @@ -119,6 +121,7 @@ parser: dhcp4_lexer.cc location.hh position.hh stack.hh dhcp4_parser.cc dhcp4_pa # Note C++11 deprecated register still used by flex < 2.6.0 location.hh position.hh stack.hh dhcp4_parser.cc dhcp4_parser.h: dhcp4_parser.yy $(YACC) --defines=dhcp4_parser.h --report=all --report-file=dhcp4_parser.report -o dhcp4_parser.cc dhcp4_parser.yy + $(YACC) --graph --defines=dhcp4_parser.h --report=all --report-file=dhcp4_parser.report -o dhcp4_parser.cc dhcp4_parser.yy dhcp4_lexer.cc: dhcp4_lexer.ll $(LEX) --prefix parser4_ -o dhcp4_lexer.cc dhcp4_lexer.ll diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 6f62bd5642..080112f4e6 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include + #include #include #include @@ -14,12 +15,17 @@ #include #include #include +#include #include +#include #include #include #include #include + #include + +#include #include using namespace isc::data; @@ -119,8 +125,6 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) { // configuration from a JSON file. isc::data::ConstElementPtr json; - isc::data::ConstElementPtr dhcp4; - isc::data::ConstElementPtr logger; isc::data::ConstElementPtr result; // Basic sanity check: file name must not be empty. @@ -231,7 +235,7 @@ ControlledDhcpv4Srv::commandConfigReloadHandler(const string&, LOG_ERROR(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION_FAIL) .arg(file); return (createAnswer(CONTROL_RESULT_ERROR, - "Config reload failed:" + string(ex.what()))); + "Config reload failed: " + string(ex.what()))); } } @@ -560,12 +564,11 @@ ControlledDhcpv4Srv::processCommand(const string& command, return (srv->commandConfigWriteHandler(command, args)); } - ConstElementPtr answer = isc::config::createAnswer(1, - "Unrecognized command:" + command); - return (answer); + return(isc::config::createAnswer(1, "Unrecognized command: " + + command)); } catch (const Exception& ex) { - return (isc::config::createAnswer(1, "Error while processing command '" - + command + "':" + ex.what() + + return (isc::config::createAnswer(1, "Error while processing command '" + + command + "': " + ex.what() + ", params: '" + txt + "'")); } } @@ -576,6 +579,114 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED) .arg(config->str()); + ConstElementPtr answer = configureDhcp4ServerId(config); + // Check that configuration was successful. + try { + int rcode = 0; + isc::config::parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, "Failed to process server identifier: " + + string(ex.what()))); + } + + // Parse (and store in config manager) the 'configuration-type' parameter + // in order to decide if the Dhcp4 configuration will be read from the provided + // 'config' argument or from database + answer = configureDhcp4ServerConfigSource(config); + + // Check that configuration was successful. + try { + int rcode = 0; + isc::config::parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, + "Failed to process server configuration type: " + + string(ex.what()))); + } + + auto stagingCfg = CfgMgr::instance().getStagingCfg(); + CfgSrvConfigType& configurationType = stagingCfg->getConfigurationType(); + + // Read master/shard configuration into dhcpConfig. + ConstElementPtr interfacesConfig; + if (configurationType.type_ == CfgSrvConfigType::MASTER_DATABASE) { + ElementPtr dhcpConfig; + ElementPtr databaseLogging; + SrvConfigMasterInfoPtr configData; + try { + ConstElementPtr answer = + readDhcpv4SrvConfigFromMasterDatabase(dhcpConfig, databaseLogging, configData); + int rcode = 0; + parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + + if (configData == SrvConfigMasterInfoPtr()) { + isc_throw(Unexpected, "Null master configuration data returned"); + } + } catch (const exception& ex) { + return createAnswer(1, "Failed to process master database server configuration: " + + string(ex.what())); + } + + // If there's no logging element, we'll just pass NULL pointer, + // which will be handled by configureLogger(). + Daemon::configureLogger(databaseLogging, stagingCfg); + + // Apply the new logger configuration to log4cplus. + stagingCfg->applyLoggingCfg(); + + ConstElementPtr configDatabase = Element::fromJSON(configData->config_database_); + if (configDatabase) { + // Update the config manager with the server configuration type + configurationType.type_ = CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr stagingCfgDbAccess = stagingCfg->getCfgDbAccess(); + parser.parse(stagingCfgDbAccess, configDatabase); + } + + interfacesConfig = dhcpConfig->get("interfaces-config"); + } else if (configurationType.type_ == CfgSrvConfigType::CONFIG_DATABASE) { + interfacesConfig = config->get("interfaces-config"); + } + + // Read database configuration and apply it to the current configuration. + if (configurationType.type_ == CfgSrvConfigType::MASTER_DATABASE || + configurationType.type_ == CfgSrvConfigType::CONFIG_DATABASE) { + if (!interfacesConfig) { + return createAnswer( + 1, "no mandatory 'interfaces-config' entry in the local configuration"); + } + + ElementPtr databaseConfig; + try { + ConstElementPtr answer = readDhcpv4SrvConfigFromDatabase(databaseConfig); + int rcode = 0; + parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + } catch (const exception& ex) { + return createAnswer(1, + "Failed to process configuration database server configuration: " + + string(ex.what())); + } + + // Keep the 'interfaces-config' settings from the config file received at startup + databaseConfig->set("interfaces-config", interfacesConfig); + + // The configuration for 'Dhcp4' will now point to the one read from the database. + config = ConstElementPtr(databaseConfig); + } + ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance(); // Single stream instance used in all error clauses @@ -586,7 +697,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { return (isc::config::createAnswer(1, err.str())); } - ConstElementPtr answer = configureDhcp4Server(*srv, config); + answer = configureDhcp4Server(*srv, config); // Check that configuration was successful. If not, do not reopen sockets // and don't bother with DDNS stuff. @@ -597,7 +708,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - err << "Failed to process configuration:" << ex.what(); + err << "Failed to process configuration: " << ex.what(); return (isc::config::createAnswer(1, err.str())); } @@ -819,6 +930,146 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { // this stage. } +isc::data::ConstElementPtr +ControlledDhcpv4Srv::readDhcpv4SrvConfigFromMasterDatabase( + isc::data::ElementPtr& db_local_config, + isc::data::ElementPtr& db_logging, + SrvConfigMasterInfoPtr& configData) { + + std::string server_id = CfgMgr::instance().getStagingCfg()->getInstanceId(); + + // Re-open configuration database with new parameters. + try { + CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess(); + cfg_db->setAppendedParameters("universe=4"); + cfg_db->createSrvMasterCfgManagers(); + + // Read the contents from database + configData = SrvConfigMasterMgrFactory::instance().getConfig4(server_id); + if (configData == SrvConfigMasterInfoPtr()) { + return (isc::config::createAnswer(1, + "No entry found in master database for server id: " + + server_id )); + } + + CfgMgr::instance().getStagingCfg()->setMasterServerCfgTimestamp(configData->timestamp_); + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, "Unable to open database: " + + std::string(ex.what()))); + } + + // Parse the contents as JSON + isc::data::ConstElementPtr json; + json = isc::data::Element::fromJSON(configData->server_config_, true); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, "Configuration from database is expected to be " + "a map, i.e., start with { and end with } and contain " + "at least an entry called 'Dhcp4' that itself is a map. " + "The database contains a valid JSON, but its top element is not a map." + " Did you forget to add { } around your configuration?"); + } + + try { + isc::data::ConstElementPtr dhcp4; + // Get Dhcp4 component from the config + dhcp4 = json->get("Dhcp4"); + + if (!dhcp4) { + isc_throw(isc::BadValue, "no mandatory 'Dhcp4' entry in" + " the configuration"); + } + + // Replace the dhcp4 current config with the one read from database + db_local_config = boost::const_pointer_cast(dhcp4); + + isc::data::ConstElementPtr logging; + // Get Logging component from the config + logging = json->get("Logging"); + + if (logging) { + // Replace the Logging current config with the one read from database + db_logging = boost::const_pointer_cast(logging); + } else { + db_logging = isc::data::ElementPtr(); + } + + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, + "Server Configuration error using using database: " + + std::string(ex.what()))); + } + + return (isc::config::createAnswer(0, + "Server Configuration successfully loaded from database.")); +} + +isc::data::ConstElementPtr +ControlledDhcpv4Srv::readDhcpv4SrvConfigFromDatabase(isc::data::ElementPtr& db_config) { + SrvConfigInfoPtr configData; + + // Re-open configuration database with new parameters. + try { + CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess(); + cfg_db->setAppendedParameters("universe=4"); + cfg_db->createSrvCfgManagers(); + + // Read the contents from database + configData = SrvConfigMgrFactory::instance().getJsonConfig4(); + if (configData == SrvConfigInfoPtr()) { + return (isc::config::createAnswer(1, "No entry found in database: ")); + } + CfgMgr::instance().getStagingCfg()->setServerCfgTimestamp(configData->timestamp_); + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, "Unable to open database: " + + std::string(ex.what()))); + } + + // Parse the contents as JSON + isc::data::ConstElementPtr json; + json = isc::data::Element::fromJSON(configData->json_data_, true); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, "Configuration from database is expected to be " + "a map, i.e., start with { and end with } and contain " + "at least an entry called 'Dhcp4' that itself is a map. " + "The database contains a valid JSON, but its top element is not a map." + " Did you forget to add { } around your configuration?"); + } + + try { + isc::data::ConstElementPtr dhcp4; + // Get Dhcp4 component from the config + dhcp4 = json->get("Dhcp4"); + + if (!dhcp4) { + isc_throw(isc::BadValue, "no mandatory 'Dhcp4' entry in" + " the configuration"); + } + + // Replace the dhcp4 current config with the one read from database + db_config = boost::const_pointer_cast(dhcp4); + + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, + "Server Configuration error using using database: " + + std::string(ex.what()))); + } + + return (isc::config::createAnswer(0, + "Server Configuration successfully loaded from database.")); +} + void ControlledDhcpv4Srv::sessionReader(void) { // Process one asio event. If there are more events, iface_mgr will call // this callback more than once. @@ -921,5 +1172,5 @@ ControlledDhcpv4Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) { return(true); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 06d5ee6472..2afc95e839 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include namespace isc { namespace dhcp { @@ -121,10 +123,43 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { private: + /// @brief Retrieves the server configuration information from the master database. + /// + /// Retrieves the server configuration information from the master database. + /// The information contains server specific settings and the shard credentials + /// where resides the rest of the configuration (common configuration for all servers) + /// + /// + /// @param dhcp4_config On input contains the current dhcp4 configuration in json format. + /// On output contains the new configuration read from database (also in json format) + /// + /// @return answer that contains result of the reconfiguration. + /// @throw Dhcp4ConfigError if trying to create a parser for NULL config. + static isc::data::ConstElementPtr + readDhcpv4SrvConfigFromMasterDatabase(isc::data::ElementPtr& db_local_config, + data::ElementPtr& db_logging, + SrvConfigMasterInfoPtr& configData); + + /// @brief Retrieves the server configuration information from database + /// and updates the current configuration. + /// + /// Retrieves the server configuration information from database + /// and replaces the current configuration received as paramater with the + /// new one. + /// + /// + /// @param db_config On input contains the current dhcp4 configuration in json format. + /// On output contains the new configuration read from database (also in json format) + /// + /// @return answer that contains result of the reconfiguration. + /// @throw Dhcp4ConfigError if trying to create a parser for NULL config. + static isc::data::ConstElementPtr + readDhcpv4SrvConfigFromDatabase(data::ElementPtr& db_config); + /// @brief Callback that will be called from iface_mgr when data /// is received over control socket. /// - /// This static callback method is called from IfaceMgr::receive6() method, + /// This static callback method is called from IfaceMgr::receive4() method, /// when there is a new command or configuration sent over control socket /// (that was sent from some yet unspecified sender). static void sessionReader(void); @@ -374,7 +409,7 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { TimerMgrPtr timer_mgr_; }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif diff --git a/src/bin/dhcp4/dhcp4_lexer.ll b/src/bin/dhcp4/dhcp4_lexer.ll index 6650d75f95..e14e183187 100644 --- a/src/bin/dhcp4/dhcp4_lexer.ll +++ b/src/bin/dhcp4/dhcp4_lexer.ll @@ -36,7 +36,7 @@ unsigned int comment_start_line = 0; using namespace isc::dhcp; -}; +} /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::dhcp::Parser4Context::fatal(msg) @@ -267,6 +267,42 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } +\"configuration-type\" { + switch(driver.ctx_) { + case isc::dhcp::Parser4Context::DHCP4: + return isc::dhcp::Dhcp4Parser::make_CONFIGURATION_TYPE(driver.loc_); + default: + return isc::dhcp::Dhcp4Parser::make_STRING("configuration-type", driver.loc_); + } +} + +\"instance-id\" { + switch(driver.ctx_) { + case isc::dhcp::Parser4Context::DHCP4: + return isc::dhcp::Dhcp4Parser::make_INSTANCE_ID(driver.loc_); + default: + return isc::dhcp::Dhcp4Parser::make_STRING("instance-id", driver.loc_); + } +} + +\"master-database\" { + switch(driver.ctx_) { + case isc::dhcp::Parser4Context::DHCP4: + return isc::dhcp::Dhcp4Parser::make_MASTER_DATABASE(driver.loc_); + default: + return isc::dhcp::Dhcp4Parser::make_STRING("master-database", driver.loc_); + } +} + +\"config-database\" { + switch(driver.ctx_) { + case isc::dhcp::Parser4Context::DHCP4: + return isc::dhcp::Dhcp4Parser::make_CONFIG_DATABASE(driver.loc_); + default: + return isc::dhcp::Dhcp4Parser::make_STRING("config-database", driver.loc_); + } +} + \"re-detect\" { switch(driver.ctx_) { case isc::dhcp::Parser4Context::INTERFACES_CONFIG: @@ -314,6 +350,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"type\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: case isc::dhcp::Parser4Context::OPTION_DEF: @@ -361,6 +399,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"user\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_USER(driver.loc_); @@ -371,6 +411,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"password\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_PASSWORD(driver.loc_); @@ -381,6 +423,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"host\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_HOST(driver.loc_); @@ -391,6 +435,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"port\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_PORT(driver.loc_); @@ -401,6 +447,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"persist\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_PERSIST(driver.loc_); @@ -411,6 +459,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"lfc-interval\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_LFC_INTERVAL(driver.loc_); @@ -419,28 +469,22 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } -\"connect-timeout\" { - switch(driver.ctx_) { - case isc::dhcp::Parser4Context::LEASE_DATABASE: - case isc::dhcp::Parser4Context::HOSTS_DATABASE: - return isc::dhcp::Dhcp4Parser::make_CONNECT_TIMEOUT(driver.loc_); - default: - return isc::dhcp::Dhcp4Parser::make_STRING("connect-timeout", driver.loc_); - } -} - -\"keyspace\" { +\"tcp-nodelay\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: - return isc::dhcp::Dhcp4Parser::make_KEYSPACE(driver.loc_); + return isc::dhcp::Dhcp4Parser::make_TCP_NODELAY(driver.loc_); default: - return isc::dhcp::Dhcp4Parser::make_STRING("keyspace", driver.loc_); + return isc::dhcp::Dhcp4Parser::make_STRING("tcp-nodelay", driver.loc_); } } \"reconnect-wait-time\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_RECONNECT_WAIT_TIME(driver.loc_); @@ -451,6 +495,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"request-timeout\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_REQUEST_TIMEOUT(driver.loc_); @@ -461,6 +507,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"tcp-keepalive\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_TCP_KEEPALIVE(driver.loc_); @@ -469,18 +517,34 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } -\"tcp-nodelay\" { +\"connect-timeout\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: - return isc::dhcp::Dhcp4Parser::make_TCP_NODELAY(driver.loc_); + return isc::dhcp::Dhcp4Parser::make_CONNECT_TIMEOUT(driver.loc_); default: - return isc::dhcp::Dhcp4Parser::make_STRING("tcp-nodelay", driver.loc_); + return isc::dhcp::Dhcp4Parser::make_STRING("connect-timeout", driver.loc_); + } +} + +\"keyspace\" { + switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: + case isc::dhcp::Parser4Context::LEASE_DATABASE: + case isc::dhcp::Parser4Context::HOSTS_DATABASE: + return isc::dhcp::Dhcp4Parser::make_KEYSPACE(driver.loc_); + default: + return isc::dhcp::Dhcp4Parser::make_STRING("keyspace", driver.loc_); } } \"contact-points\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_CONTACT_POINTS(driver.loc_); @@ -491,6 +555,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"max-reconnect-tries\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: return isc::dhcp::Dhcp4Parser::make_MAX_RECONNECT_TRIES(driver.loc_); @@ -586,6 +652,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"name\" { switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: case isc::dhcp::Parser4Context::LEASE_DATABASE: case isc::dhcp::Parser4Context::HOSTS_DATABASE: case isc::dhcp::Parser4Context::OPTION_DEF: @@ -1070,7 +1138,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } - \"parameters\" { switch(driver.ctx_) { case isc::dhcp::Parser4Context::HOOKS_LIBRARIES: @@ -1526,8 +1593,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } - - {JSONString} { /* A string has been matched. It contains the actual string and single quotes. We need to get those quotes out of the way and just use its content, e.g. @@ -1820,5 +1885,5 @@ class Dummy { /* cppcheck-suppress unusedPrivateFunction */ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); } }; -} +} // namespace #endif /* !__clang_analyzer__ */ diff --git a/src/bin/dhcp4/dhcp4_log.cc b/src/bin/dhcp4/dhcp4_log.cc index 4f82a1b3a9..b1d48e2b5c 100644 --- a/src/bin/dhcp4/dhcp4_log.cc +++ b/src/bin/dhcp4/dhcp4_log.cc @@ -14,6 +14,15 @@ namespace isc { namespace dhcp { +const int DBG_DHCP4_START = isc::log::DBGLVL_START_SHUT; +const int DBG_DHCP4_SHUT = isc::log::DBGLVL_START_SHUT; +const int DBG_DHCP4_COMMAND = isc::log::DBGLVL_COMMAND; +const int DBG_DHCP4_BASIC = isc::log::DBGLVL_TRACE_BASIC; +const int DBG_DHCP4_HOOKS = isc::log::DBGLVL_TRACE_BASIC; +const int DBG_DHCP4_BASIC_DATA = isc::log::DBGLVL_TRACE_BASIC_DATA; +const int DBG_DHCP4_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +const int DBG_DHCP4_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; + const char* DHCP4_ROOT_LOGGER_NAME = "kea-dhcp4"; const char* DHCP4_APP_LOGGER_NAME = "dhcp4"; const char* DHCP4_BAD_PACKET_LOGGER_NAME = "bad-packets"; @@ -29,6 +38,6 @@ isc::log::Logger options4_logger(DHCP4_OPTIONS_LOGGER_NAME); isc::log::Logger ddns4_logger(DHCP4_DDNS_LOGGER_NAME); isc::log::Logger lease4_logger(DHCP4_LEASE_LOGGER_NAME); -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp4/dhcp4_log.h b/src/bin/dhcp4/dhcp4_log.h index 34a790cb39..02fa7aad8e 100644 --- a/src/bin/dhcp4/dhcp4_log.h +++ b/src/bin/dhcp4/dhcp4_log.h @@ -21,19 +21,19 @@ namespace dhcp { //@{ /// @brief Debug level used to log information during server startup. -const int DBG_DHCP4_START = isc::log::DBGLVL_START_SHUT; +extern const int DBG_DHCP4_START; /// @brief Debug level used to log information during server shutdown. -const int DBG_DHCP4_SHUT = isc::log::DBGLVL_START_SHUT; +extern const int DBG_DHCP4_SHUT; /// @brief Debug level used to log receiving commands. -const int DBG_DHCP4_COMMAND = isc::log::DBGLVL_COMMAND; +extern const int DBG_DHCP4_COMMAND; /// @brief Debug level used to trace basic operations within the code. -const int DBG_DHCP4_BASIC = isc::log::DBGLVL_TRACE_BASIC; +extern const int DBG_DHCP4_BASIC; /// @brief Debug level used to trace hook related operations -const int DBG_DHCP4_HOOKS = isc::log::DBGLVL_TRACE_BASIC; +extern const int DBG_DHCP4_HOOKS; /// @brief Debug level used to log the traces with some basic data. /// @@ -42,7 +42,7 @@ const int DBG_DHCP4_HOOKS = isc::log::DBGLVL_TRACE_BASIC; /// more detailed information in cases when it is warranted and the /// extraction of the data doesn't impact the server's performance /// significantly. -const int DBG_DHCP4_BASIC_DATA = isc::log::DBGLVL_TRACE_BASIC_DATA; +extern const int DBG_DHCP4_BASIC_DATA; /// @brief Debug level used to trace detailed errors. /// @@ -50,10 +50,10 @@ const int DBG_DHCP4_BASIC_DATA = isc::log::DBGLVL_TRACE_BASIC_DATA; /// packets. (These are not logged at severities of WARN or higher for fear /// that a set of deliberately invalid packets set to the server could overwhelm /// the logging.) -const int DBG_DHCP4_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +extern const int DBG_DHCP4_DETAIL; /// @brief This level is used to log the contents of packets received and sent. -const int DBG_DHCP4_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; +extern const int DBG_DHCP4_DETAIL_DATA; //@} @@ -105,7 +105,7 @@ extern isc::log::Logger packet4_logger; /// @brief Logger for options parser. /// /// This logger is used to issue log messages related to processing of the -/// DHCP options +/// DHCP options extern isc::log::Logger options4_logger; /// @brief Logger for Hostname or FQDN processing. @@ -121,7 +121,7 @@ extern isc::log::Logger lease4_logger; //@} -} // namespace dhcp4 -} // namespace isc +} // namespace dhcp4 +} // namespace isc #endif // DHCP4_LOG_H diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index 22362d2259..11ccbef9e8 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -233,7 +233,7 @@ the message. % DHCP4_DHCP4O6_RECEIVING receiving DHCPv4o6 packet from DHCPv6 server This debug message is printed when the server is receiving a DHCPv4o6 -from the DHCPv6 server over inter-process communication socket. +from the DHCPv4 server over inter-process communication socket. % DHCP4_DHCP4O6_RESPONSE_DATA %1: responding with packet %2 (type %3), packet details: %4 A debug message including the detailed data about the packet being @@ -763,10 +763,15 @@ will drop its message if the received message was DHCPDISCOVER, and will send DHCPNAK if the received message was DHCPREQUEST. The argument includes the client and the transaction identification information. -<<<<<<< HEAD -======= -% DHCP6_DHCP4O6_PACKET_RECEIVED received DHCPv4o6 packet from DHCPv6 server (type %1) for %2 port %3 on interface %4 -This debug message is printed when the server is receiving a DHCPv4o6 -from the DHCPv6 server over inter-process communication. ->>>>>>> trac5404 +% DHCP4_TRANSACTION_FAILED transaction failed. error: %1 +The transaction failed with the error message. + +% DHCP4_FORCE_RELOAD_CONFIGURATION DHCPv4 force configuration reload. +A force configuration reload event has been initiated. + +% DHCP4_SHARD_CONFIGURATION_CHANGED DHCPv4 shard configuration is different. +A different shard configuration has been detected. + +% DHCP4_MASTER_CONFIGURATION_CHANGED DHCPv4 master configuration is different. +A different master configuration has been detected. diff --git a/src/bin/dhcp4/dhcp4_parser.yy b/src/bin/dhcp4/dhcp4_parser.yy index 555cb965f3..34b1d4aecc 100644 --- a/src/bin/dhcp4/dhcp4_parser.yy +++ b/src/bin/dhcp4/dhcp4_parser.yy @@ -66,6 +66,11 @@ using namespace std; SERVER_HOSTNAME "server-hostname" BOOT_FILE_NAME "boot-file-name" + CONFIGURATION_TYPE "configuration-type" + INSTANCE_ID "instance-id" + + MASTER_DATABASE "master-database" + CONFIG_DATABASE "config-database" LEASE_DATABASE "lease-database" HOSTS_DATABASE "hosts-database" HOSTS_DATABASES "hosts-databases" @@ -82,13 +87,13 @@ using namespace std; LFC_INTERVAL "lfc-interval" READONLY "readonly" CONNECT_TIMEOUT "connect-timeout" - CONTACT_POINTS "contact-points" - KEYSPACE "keyspace" - MAX_RECONNECT_TRIES "max-reconnect-tries" + TCP_NODELAY "tcp-nodelay" RECONNECT_WAIT_TIME "reconnect-wait-time" REQUEST_TIMEOUT "request-timeout" TCP_KEEPALIVE "tcp-keepalive" - TCP_NODELAY "tcp-nodelay" + CONTACT_POINTS "contact-points" + KEYSPACE "keyspace" + MAX_RECONNECT_TRIES "max-reconnect-tries" VALID_LIFETIME "valid-lifetime" RENEW_TIMER "renew-timer" @@ -428,6 +433,8 @@ global_param: valid_lifetime | subnet4_list | shared_networks | interfaces_config + | master_database + | config_database | lease_database | hosts_database | hosts_databases @@ -448,6 +455,8 @@ global_param: valid_lifetime | user_context | comment | unknown_map_entry + | configuration_type + | instance_id ; valid_lifetime: VALID_LIFETIME COLON INTEGER { @@ -480,7 +489,6 @@ match_client_id: MATCH_CLIENT_ID COLON BOOLEAN { ctx.stack_.back()->set("match-client-id", match); }; - interfaces_config: INTERFACES_CONFIG { ElementPtr i(new MapElement(ctx.loc2pos(@1))); ctx.stack_.back()->set("interfaces-config", i); @@ -531,6 +539,26 @@ dhcp_socket_type: DHCP_SOCKET_TYPE { ctx.leave(); }; +master_database: MASTER_DATABASE { + ElementPtr i(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("master-database", i); + ctx.stack_.push_back(i); + ctx.enter(ctx.MASTER_DATABASE); +} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +config_database: CONFIG_DATABASE { + ElementPtr i(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("config-database", i); + ctx.stack_.push_back(i); + ctx.enter(ctx.CONFIG_DATABASE); +} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + socket_type: RAW { $$ = ElementPtr(new StringElement("raw", ctx.loc2pos(@1))); } | UDP { $$ = ElementPtr(new StringElement("udp", ctx.loc2pos(@1))); } ; @@ -620,12 +648,12 @@ database_map_param: database_type | lfc_interval | readonly | connect_timeout - | contact_points - | max_reconnect_tries + | tcp_nodelay | reconnect_wait_time | request_timeout | tcp_keepalive - | tcp_nodelay + | contact_points + | max_reconnect_tries | keyspace | unknown_map_entry ; @@ -700,6 +728,16 @@ connect_timeout: CONNECT_TIMEOUT COLON INTEGER { ctx.stack_.back()->set("connect-timeout", n); }; +tcp_nodelay: TCP_NODELAY COLON BOOLEAN { + ElementPtr n(new BoolElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("tcp-nodelay", n); +}; + +reconnect_wait_time: RECONNECT_WAIT_TIME COLON INTEGER { + ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("reconnect-wait-time", n); +}; + request_timeout: REQUEST_TIMEOUT COLON INTEGER { ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); ctx.stack_.back()->set("request-timeout", n); @@ -710,11 +748,6 @@ tcp_keepalive: TCP_KEEPALIVE COLON INTEGER { ctx.stack_.back()->set("tcp-keepalive", n); }; -tcp_nodelay: TCP_NODELAY COLON BOOLEAN { - ElementPtr n(new BoolElement($3, ctx.loc2pos(@3))); - ctx.stack_.back()->set("tcp-nodelay", n); -}; - contact_points: CONTACT_POINTS { ctx.enter(ctx.NO_KEYWORD); } COLON STRING { @@ -736,11 +769,6 @@ max_reconnect_tries: MAX_RECONNECT_TRIES COLON INTEGER { ctx.stack_.back()->set("max-reconnect-tries", n); }; -reconnect_wait_time: RECONNECT_WAIT_TIME COLON INTEGER { - ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); - ctx.stack_.back()->set("reconnect-wait-time", n); -}; - host_reservation_identifiers: HOST_RESERVATION_IDENTIFIERS { ElementPtr l(new ListElement(ctx.loc2pos(@1))); ctx.stack_.back()->set("host-reservation-identifiers", l); @@ -782,6 +810,22 @@ client_id : CLIENT_ID { ctx.stack_.back()->add(client); }; +configuration_type: CONFIGURATION_TYPE { + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr prf(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("configuration-type", prf); + ctx.leave(); +}; + +instance_id: INSTANCE_ID { + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr prf(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("instance-id", prf); + ctx.leave(); +}; + flex_id: FLEX_ID { ElementPtr flex_id(new StringElement("flex-id", ctx.loc2pos(@1))); ctx.stack_.back()->add(flex_id); @@ -1918,7 +1962,7 @@ replace_client_name: REPLACE_CLIENT_NAME { replace_client_name_value: WHEN_PRESENT { - $$ = ElementPtr(new StringElement("when-present", ctx.loc2pos(@1))); + $$ = ElementPtr(new StringElement("when-present", ctx.loc2pos(@1))); } | NEVER { $$ = ElementPtr(new StringElement("never", ctx.loc2pos(@1))); diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 6b8f365e97..750f858f9f 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include + #include #include #include @@ -35,7 +36,10 @@ #include #include #include -#include +#include +#include +#include +#include #include #include #include @@ -44,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -169,7 +172,7 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, .arg(query_->getLabel()) .arg(classes.toText()); } -}; +} void Dhcpv4Exchange::initResponse() { @@ -970,54 +973,132 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp, bool allow_packet_park) { AllocEngine::ClientContext4Ptr ctx; + bool transactionError = false; + bool reloadConfiguration = false; try { - switch (query->getType()) { - case DHCPDISCOVER: - rsp = processDiscover(query); - break; + LeaseMgrFactory::instance().startTransaction(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp4_logger, DHCP4_TRANSACTION_FAILED).arg(ex.what()); + transactionError = true; + } + if (!transactionError) { + bool rollback = false; + bool apply = true; + try { + switch (query->getType()) { + case DHCPDISCOVER: + rsp = processDiscover(query); + break; - case DHCPREQUEST: - // Note that REQUEST is used for many things in DHCPv4: for - // requesting new leases, renewing existing ones and even - // for rebinding. - rsp = processRequest(query, ctx); - break; + case DHCPREQUEST: + // Note that REQUEST is used for many things in DHCPv4: for + // requesting new leases, renewing existing ones and even + // for rebinding. + rsp = processRequest(query, ctx); + break; - case DHCPRELEASE: - processRelease(query, ctx); - break; + case DHCPRELEASE: + processRelease(query, ctx); + break; - case DHCPDECLINE: - processDecline(query, ctx); - break; + case DHCPDECLINE: + processDecline(query, ctx); + break; - case DHCPINFORM: - rsp = processInform(query); - break; + case DHCPINFORM: + rsp = processInform(query); + break; - default: - // Only action is to output a message if debug is enabled, - // and that is covered by the debug statement before the - // "switch" statement. - ; - } - } catch (const std::exception& e) { + default: + // Only action is to output a message if debug is enabled, + // and that is covered by the debug statement before the + // "switch" statement. + ; + } + } catch (const TransactionException& e) { + LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_BASIC, + DHCP4_PACKET_DROP_0007) + .arg(query->getLabel()) + .arg(e.what()); - // Catch-all exception (we used to call only isc::Exception, but - // std::exception could potentially be raised and if we don't catch - // it here, it would be caught in main() and the process would - // terminate). Just log the problem and ignore the packet. - // (The problem is logged as a debug message because debug is - // disabled by default - it prevents a DDOS attack based on the - // sending of problem packets.) - LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_BASIC, - DHCP4_PACKET_DROP_0007) - .arg(query->getLabel()) - .arg(e.what()); + // Increase the statistic of dropped packets. + isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop", + static_cast(1)); + rollback = true; + apply = false; + } catch (const std::exception& e) { - // Increase the statistic of dropped packets. - isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop", - static_cast(1)); + // Catch-all exception (we used to call only isc::Exception, but + // std::exception could potentially be raised and if we don't catch + // it here, it would be caught in main() and the process would + // terminate). Just log the problem and ignore the packet. + // (The problem is logged as a debug message because debug is + // disabled by default - it prevents a DDOS attack based on the + // sending of problem packets.) + LOG_DEBUG(bad_packet4_logger, DBG_DHCP4_BASIC, + DHCP4_PACKET_DROP_0007) + .arg(query->getLabel()) + .arg(e.what()); + + // Increase the statistic of dropped packets. + isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop", + static_cast(1)); + rollback = true; + } + if (rollback) { + if (apply) { + try { + LeaseMgrFactory::instance().rollback(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp4_logger, DHCP4_TRANSACTION_FAILED).arg(ex.what()); + } + } + } else { + try { + if (SrvConfigMgrFactory::initialized()) { + SrvConfigInfoPtr configInfo = + SrvConfigMgrFactory::instance().getConfig4Timestamp(); + if (configInfo && configInfo->timestamp_ != + CfgMgr::instance().getCurrentCfg()->getServerCfgTimestamp()) { + LOG_INFO(dhcp4_logger, DHCP4_SHARD_CONFIGURATION_CHANGED); + reloadConfiguration = true; + } + } + if (SrvConfigMasterMgrFactory::initialized()) { + std::string server_id = CfgMgr::instance().getCurrentCfg()->getInstanceId(); + SrvConfigMasterInfoPtr masterConfigInfo = + SrvConfigMasterMgrFactory::instance().getMasterConfig4Timestamp(server_id); + if (masterConfigInfo && masterConfigInfo->timestamp_ != + CfgMgr::instance().getCurrentCfg()->getMasterServerCfgTimestamp()) { + LOG_INFO(dhcp4_logger, DHCP4_MASTER_CONFIGURATION_CHANGED); + reloadConfiguration = true; + } + } + } catch (const std::exception& ex) { + LOG_INFO(dhcp4_logger, DHCP4_TRANSACTION_FAILED).arg(ex.what()); + reloadConfiguration = true; + } + if (!reloadConfiguration) { + try { + LeaseMgrFactory::instance().commit(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp4_logger, DHCP4_TRANSACTION_FAILED).arg(ex.what()); + reloadConfiguration = true; + } + } else { + try { + LeaseMgrFactory::instance().rollback(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp4_logger, DHCP4_TRANSACTION_FAILED).arg(ex.what()); + } + } + } + } + + if (reloadConfiguration) { + LOG_INFO(dhcp4_logger, DHCP4_FORCE_RELOAD_CONFIGURATION); + kill(getpid(), SIGHUP); + rsp = Pkt4Ptr(); } bool packet_park = false; @@ -3558,5 +3639,5 @@ void Dhcpv4Srv::discardPackets() { HooksManager::clearParkingLots(); } -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index fd8c671f6c..c1117316e9 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -29,6 +29,7 @@ #include #include #include +#include // Undefine the macro OPTIONAL which is defined in some operating // systems but conflicts with a member of the RequirementLevel enum in @@ -915,7 +916,6 @@ class Dhcpv4Srv : public Daemon { bool use_bcast_; ///< Should broadcast be enabled on sockets (if true). protected: - /// @brief Holds information about disabled DHCP service and/or /// disabled subnet/network scopes. NetworkStatePtr network_state_; @@ -960,7 +960,7 @@ class Dhcpv4Srv : public Daemon { static int getHookIndexLease4Decline(); }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // DHCP4_SRV_H diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 6ee88509b0..7d67cea9ef 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -61,7 +61,6 @@ namespace { /// See @ref parse method for a list of supported parameters. class Dhcp4ConfigParser : public isc::data::SimpleParser { public: - /// @brief Sets global parameters in staging configuration /// /// @param global global configuration scope @@ -226,7 +225,7 @@ class Dhcp4ConfigParser : public isc::data::SimpleParser { } }; -} // anonymous namespace +} // namespace namespace isc { namespace dhcp { @@ -238,7 +237,7 @@ namespace dhcp { /// a command result, unless they specifically alter the channel configuration. /// In that case the user simply has to accept they'll be disconnected. /// -void configureCommandChannel() { +void configureCommandChannel4() { // Get new socket configuration. ConstElementPtr sock_cfg = CfgMgr::instance().getStagingCfg()->getControlSocketInfo(); @@ -442,6 +441,20 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, continue; } + if (config_pair.first == "master-database") { + DbAccessParser parser(DBType::MASTER_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_pair.second); + continue; + } + + if (config_pair.first == "config-database") { + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_pair.second); + continue; + } + if (config_pair.first == "subnet4") { SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); Subnets4ListConfigParser subnets_parser; @@ -451,7 +464,6 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, } if (config_pair.first == "shared-networks") { - /// We need to create instance of SharedNetworks4ListParser /// and parse the list of the shared networks into the /// CfgSharedNetworks4 object. One additional step is then to @@ -532,11 +544,11 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, // configuration. This will add created subnets and option values into // the server's configuration. // This operation should be exception safe but let's make sure. - if (!rollback) { + if (!rollback && IfaceMgr::instance().isServerMode()) { try { // Setup the command channel. - configureCommandChannel(); + configureCommandChannel4(); // No need to commit interface names as this is handled by the // CfgMgr::commit() function. @@ -583,5 +595,81 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, return (answer); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +isc::data::ConstElementPtr +configureDhcp4ServerId(isc::data::ConstElementPtr config_set) { + ConstElementPtr answer; + + // Check if an 'instance-id' parameter is provided. + ConstElementPtr server_id = config_set->get("instance-id"); + if (server_id) { + std::string id; + if (server_id->getValue(id)) { + CfgMgr::instance().getStagingCfg()->getInstanceId() = id; + } + } + + // Everything was fine. Configuration is successful. + answer = isc::config::createAnswer(0, "Configuration successful."); + return (answer); +} + +isc::data::ConstElementPtr +configureDhcp4ServerConfigSource(isc::data::ConstElementPtr config_set) { + ConstElementPtr answer; + + SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + + // check if a 'configuration-type' parameter is provided + ConstElementPtr cfg_type_elem = config_set->get("configuration-type"); + if (cfg_type_elem) { + std::string type; + if (cfg_type_elem->getValue(type)) { + // Valid values for "configuration-type" : "database" and "file" + if (type == "database") { + // The configuration type specifies that the server configuration should be + // read from the database. + + // Parse the 'master-database' parameter in order to update the config manager + // with the configuration database type and credentials. + ConstElementPtr config_database = config_set->get("master-database"); + if (config_database) { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::MASTER_DATABASE; + + DbAccessParser parser(DBType::MASTER_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_database); + } else { + // There has not been specified a "master-database" parameter. + // Check if a "config-database" parameter has been specified + ConstElementPtr config_database = config_set->get("config-database"); + if (config_database) { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_database); + } + } + } else if (type == "file") { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::FILE; + } else { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::UNKNOWN; + + answer = isc::config::createAnswer(1, + "Invalid value for 'configuration-type' parameter, can't process config."); + return (answer); + } + } + } + + // Everything was fine. Configuration is successful. + answer = isc::config::createAnswer(0, "Configuration successful."); + return (answer); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp4/json_config_parser.h b/src/bin/dhcp4/json_config_parser.h index 7687e8983b..babb4f5ba4 100644 --- a/src/bin/dhcp4/json_config_parser.h +++ b/src/bin/dhcp4/json_config_parser.h @@ -59,7 +59,33 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set, bool check_only = false); -}; // end of isc::dhcp namespace -}; // end of isc namespace +/// @brief Configures the server identifier. +/// +/// This function is called every time a new configuration is received. +/// +/// @return answer that contains result of the reconfiguration. +isc::data::ConstElementPtr +configureDhcp4ServerId(data::ConstElementPtr config_set); + +/// @brief Configures the location from where the server configuration should be +/// read +/// +/// This function is called every time a new configuration is received. +/// +/// This method does not throw. It catches all exceptions and returns them as +/// reconfiguration statuses. It may return the following response codes: +/// 0 - configuration successful +/// 1 - malformed configuration (parsing failed) +/// 2 - commit failed (parsing was successful, but the values could not be +/// stored in the configuration). +/// +/// @param config_set a new configuration for DHCPv4 server. +/// @return answer that contains result of the reconfiguration. +/// @throw Dhcp4ConfigError if trying to create a parser for NULL config. +isc::data::ConstElementPtr +configureDhcp4ServerConfigSource(data::ConstElementPtr config_set); + +} // namespace dhcp +} // namespace isc #endif // DHCP4_CONFIG_PARSER_H diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc index b1997db0a8..01325d43b1 100644 --- a/src/bin/dhcp4/main.cc +++ b/src/bin/dhcp4/main.cc @@ -53,7 +53,7 @@ usage() { << "(useful for testing only)" << endl; exit(EXIT_FAILURE); } -} // end of anonymous namespace +} // namespace int main(int argc, char* argv[]) { diff --git a/src/bin/dhcp4/parser_context.cc b/src/bin/dhcp4/parser_context.cc index d160a2f6c7..de28969fb6 100644 --- a/src/bin/dhcp4/parser_context.cc +++ b/src/bin/dhcp4/parser_context.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/dhcp4/parser_context.h b/src/bin/dhcp4/parser_context.h index e0beff259a..b5d49f60a2 100644 --- a/src/bin/dhcp4/parser_context.h +++ b/src/bin/dhcp4/parser_context.h @@ -214,6 +214,12 @@ class Parser4Context /// Used while parsing Dhcp4/interfaces structures. INTERFACES_CONFIG, + /// Used while parsing Dhcp4/master-database structures. + MASTER_DATABASE, + + /// Used while parsing Dhcp4/config-database structures. + CONFIG_DATABASE, + /// Used while parsing Dhcp4/interfaces/dhcp-socket-type structures. DHCP_SOCKET_TYPE, @@ -226,6 +232,12 @@ class Parser4Context /// Used while parsing Dhcp4/hosts-database[s] structures. HOSTS_DATABASE, + /// Used while parsing Dhcp4/configuration-type structures. + CONFIGURATION_TYPE, + + /// Used while parsing Dhcp4/instance-id structures. + INSTANCE_ID, + /// Used while parsing Dhcp4/*-database/type. DATABASE_TYPE, @@ -364,7 +376,7 @@ class Parser4Context isc::data::ElementPtr parseCommon(); }; -}; // end of isc::eval namespace -}; // end of isc namespace +} // namespace eval +} // namespace isc #endif diff --git a/src/bin/dhcp4/tests/callout_library_3.cc b/src/bin/dhcp4/tests/callout_library_3.cc index 4adafb09a6..fdd1730fc1 100644 --- a/src/bin/dhcp4/tests/callout_library_3.cc +++ b/src/bin/dhcp4/tests/callout_library_3.cc @@ -9,6 +9,8 @@ /// hook point. /// static const int LIBRARY_NUMBER = 3; +#include + #include #include #include diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 24fe8d74d7..54a5fb0bb0 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -92,7 +92,7 @@ class NakedControlledDhcpv4Srv: public ControlledDhcpv4Srv { /// @brief Default control connection timeout. const size_t DEFAULT_CONNECTION_TIMEOUT = 10; -/// @brief Fixture class intended for testin control channel in the DHCPv4Srv +/// @brief Fixture class intended for testing control channel in the DHCPv4Srv class CtrlChannelDhcpv4SrvTest : public ::testing::Test { public: diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 324bd0f74c..2a37fb8196 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -4302,4 +4302,4 @@ TEST_F(Dhcpv4SrvTest, truncatedVIVSOOption) { /// @todo: Implement proper tests for MySQL lease/host database, /// see ticket #4214. -}; // end of anonymous namespace +} // namespace diff --git a/src/bin/dhcp4/tests/hooks_unittest.cc b/src/bin/dhcp4/tests/hooks_unittest.cc index 1e0898776c..b936f7e17f 100644 --- a/src/bin/dhcp4/tests/hooks_unittest.cc +++ b/src/bin/dhcp4/tests/hooks_unittest.cc @@ -720,7 +720,7 @@ class HooksDhcpv4SrvTest : public Dhcpv4SrvTest { std::vector id_test; handle.getArgument("id_value", id_test); - std::vector id = { 0x66, 0x6f, 0x6f }; // foo + std::vector id = { 0x66, 0x6f, 0x6f }; // foo handle.setArgument("id_value", id); handle.setArgument("id_type", Host::IDENT_FLEX); diff --git a/src/bin/dhcp4/tests/parser_unittest.cc b/src/bin/dhcp4/tests/parser_unittest.cc index b73cf22578..b9eefc3467 100644 --- a/src/bin/dhcp4/tests/parser_unittest.cc +++ b/src/bin/dhcp4/tests/parser_unittest.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 88cfdec69e..739c84cdab 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -64,6 +64,9 @@ libdhcp6_la_SOURCES += dhcp6to4_ipc.cc dhcp6to4_ipc.h libdhcp6_la_SOURCES += dhcp6_lexer.ll location.hh position.hh stack.hh libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h libdhcp6_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h + +libdhcp6_la_LIBADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la + nodist_libdhcp6_la_SOURCES = dhcp6_messages.h dhcp6_messages.cc EXTRA_DIST += dhcp6_messages.mes @@ -119,6 +122,7 @@ parser: dhcp6_lexer.cc location.hh position.hh stack.hh dhcp6_parser.cc dhcp6_pa # Note C++11 deprecated register still used by flex < 2.6.0 location.hh position.hh stack.hh dhcp6_parser.cc dhcp6_parser.h: dhcp6_parser.yy $(YACC) --defines=dhcp6_parser.h --report=all --report-file=dhcp6_parser.report -o dhcp6_parser.cc dhcp6_parser.yy + $(YACC) --graph --defines=dhcp6_parser.h --report=all --report-file=dhcp6_parser.report -o dhcp6_parser.cc dhcp6_parser.yy dhcp6_lexer.cc: dhcp6_lexer.ll $(LEX) --prefix parser6_ -o dhcp6_lexer.cc dhcp6_lexer.ll diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index e50ed9fef9..c923cdba8f 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -5,13 +5,16 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include + #include #include #include #include #include +#include #include #include +#include #include #include #include @@ -20,7 +23,10 @@ #include #include #include + #include + +#include #include using namespace isc::config; @@ -95,8 +101,6 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) { // configuration from a JSON file. isc::data::ConstElementPtr json; - isc::data::ConstElementPtr dhcp6; - isc::data::ConstElementPtr logger; isc::data::ConstElementPtr result; // Basic sanity check: file name must not be empty. @@ -234,7 +238,7 @@ ControlledDhcpv6Srv::commandConfigReloadHandler(const string&, LOG_ERROR(dhcp6_logger, DHCP6_DYNAMIC_RECONFIGURATION_FAIL) .arg(file); return (createAnswer(CONTROL_RESULT_ERROR, - "Config reload failed:" + string(ex.what()))); + "Config reload failed: " + string(ex.what()))); } } @@ -562,13 +566,12 @@ ControlledDhcpv6Srv::processCommand(const std::string& command, return (srv->commandConfigWriteHandler(command, args)); } - - return (isc::config::createAnswer(1, "Unrecognized command:" - + command)); - + return (isc::config::createAnswer(1, "Unrecognized command: " + + command)); } catch (const Exception& ex) { - return (isc::config::createAnswer(1, "Error while processing command '" - + command + "':" + ex.what())); + return (isc::config::createAnswer(1, "Error while processing command '" + + command + "': " + ex.what() + + ", params: '" + txt + "'")); } } @@ -578,6 +581,114 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_RECEIVED) .arg(config->str()); + ConstElementPtr answer = configureDhcp6ServerId(config); + // Check that configuration was successful. + try { + int rcode = 0; + isc::config::parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, "Failed to process server identifier: " + + string(ex.what()))); + } + + // Parse (and store in config manager) the 'configuration-type' parameter + // in order to decide if the Dhcp6 configuration will be read from the provided + // 'config' argument or from database + answer = configureDhcp6ServerConfigSource(config); + + // Check that configuration was successful. + try { + int rcode = 0; + isc::config::parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, + "Failed to process server configuration type: " + + string(ex.what()))); + } + + auto stagingCfg = CfgMgr::instance().getStagingCfg(); + CfgSrvConfigType& configurationType = stagingCfg->getConfigurationType(); + + // Read master/shard configuration into dhcpConfig. + ConstElementPtr interfacesConfig; + if (configurationType.type_ == CfgSrvConfigType::MASTER_DATABASE) { + ElementPtr dhcpConfig; + ElementPtr databaseLogging; + SrvConfigMasterInfoPtr configData; + try { + ConstElementPtr answer = + readDhcpv6SrvConfigFromMasterDatabase(dhcpConfig, databaseLogging, configData); + int rcode = 0; + parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + + if (configData == SrvConfigMasterInfoPtr()) { + isc_throw(Unexpected, "Null master configuration data returned"); + } + } catch (const exception& ex) { + return createAnswer(1, "Failed to process master database server configuration: " + + string(ex.what())); + } + + // If there's no logging element, we'll just pass NULL pointer, + // which will be handled by configureLogger(). + Daemon::configureLogger(databaseLogging, stagingCfg); + + // Apply the new logger configuration to log4cplus. + stagingCfg->applyLoggingCfg(); + + ConstElementPtr configDatabase = Element::fromJSON(configData->config_database_); + if (configDatabase) { + // Update the config manager with the server configuration type + configurationType.type_ = CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr stagingCfgDbAccess = stagingCfg->getCfgDbAccess(); + parser.parse(stagingCfgDbAccess, configDatabase); + } + + interfacesConfig = dhcpConfig->get("interfaces-config"); + } else if (configurationType.type_ == CfgSrvConfigType::CONFIG_DATABASE) { + interfacesConfig = config->get("interfaces-config"); + } + + // Read database configuration and apply it to the current configuration. + if (configurationType.type_ == CfgSrvConfigType::MASTER_DATABASE || + configurationType.type_ == CfgSrvConfigType::CONFIG_DATABASE) { + if (!interfacesConfig) { + return createAnswer( + 1, "no mandatory 'interfaces-config' entry in the local configuration"); + } + + ElementPtr databaseConfig; + try { + ConstElementPtr answer = readDhcpv6SrvConfigFromDatabase(databaseConfig); + int rcode = 0; + parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + } catch (const exception& ex) { + return createAnswer(1, + "Failed to process configuration database server configuration: " + + string(ex.what())); + } + + // Keep the 'interfaces-config' settings from the config file received at startup + databaseConfig->set("interfaces-config", interfacesConfig); + + // The configuration for 'Dhcp6' will now point to the one read from the database. + config = ConstElementPtr(databaseConfig); + } + ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); if (!srv) { @@ -587,7 +698,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { return (no_srv); } - ConstElementPtr answer = configureDhcp6Server(*srv, config); + answer = configureDhcp6Server(*srv, config); // Check that configuration was successful. If not, do not reopen sockets // and don't bother with DDNS stuff. @@ -598,8 +709,8 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Failed to process configuration:" - + string(ex.what()))); + return (isc::config::createAnswer(1, "Failed to process configuration: " + + string(ex.what()))); } // Re-open lease and host database with new parameters. @@ -840,6 +951,146 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { // at this stage anyway. } +isc::data::ConstElementPtr +ControlledDhcpv6Srv::readDhcpv6SrvConfigFromMasterDatabase( + isc::data::ElementPtr& db_local_config, + isc::data::ElementPtr& db_logging, + SrvConfigMasterInfoPtr& configData) { + + std::string server_id = CfgMgr::instance().getStagingCfg()->getInstanceId(); + + // Re-open configuration database with new parameters. + try { + CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess(); + cfg_db->setAppendedParameters("universe=6"); + cfg_db->createSrvMasterCfgManagers(); + + // Read the contents from database + configData = SrvConfigMasterMgrFactory::instance().getConfig6(server_id); + if (configData == SrvConfigMasterInfoPtr()) { + return (isc::config::createAnswer(1, + "No entry found in master database for server id: " + + server_id )); + } + + CfgMgr::instance().getStagingCfg()->setMasterServerCfgTimestamp(configData->timestamp_); + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, "Unable to open database: " + + std::string(ex.what()))); + } + + // Parse the contents as JSON + isc::data::ConstElementPtr json; + json = isc::data::Element::fromJSON(configData->server_config_, true); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, "Configuration from database is expected to be " + "a map, i.e., start with { and end with } and contain " + "at least an entry called 'Dhcp6' that itself is a map. " + "The database contains a valid JSON, but its top element is not a map." + " Did you forget to add { } around your configuration?"); + } + + try { + isc::data::ConstElementPtr dhcp6; + // Get Dhcp6 component from the config + dhcp6 = json->get("Dhcp6"); + + if (!dhcp6) { + isc_throw(isc::BadValue, "no mandatory 'Dhcp6' entry in" + " the configuration"); + } + + // Replace the dhcp6 current config with the one read from database + db_local_config = boost::const_pointer_cast(dhcp6); + + isc::data::ConstElementPtr logging; + // Get Logging component from the config + logging = json->get("Logging"); + + if (logging) { + // Replace the Logging current config with the one read from database + db_logging = boost::const_pointer_cast(logging); + } else { + db_logging = isc::data::ElementPtr(); + } + + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, + "Server Configuration error using using database: " + + std::string(ex.what()))); + } + + return (isc::config::createAnswer(0, + "Server Configuration successfully loaded from database.")); +} + +isc::data::ConstElementPtr +ControlledDhcpv6Srv::readDhcpv6SrvConfigFromDatabase(isc::data::ElementPtr& db_config) { + SrvConfigInfoPtr configData; + + // Re-open configuration database with new parameters. + try { + CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess(); + cfg_db->setAppendedParameters("universe=6"); + cfg_db->createSrvCfgManagers(); + + // Read the contents from database + configData = SrvConfigMgrFactory::instance().getJsonConfig6(); + if (configData == SrvConfigInfoPtr()) { + return (isc::config::createAnswer(1, "No entry found in database: ")); + } + CfgMgr::instance().getStagingCfg()->setServerCfgTimestamp(configData->timestamp_); + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, "Unable to open database: " + + std::string(ex.what()))); + } + + // Parse the contents as JSON + isc::data::ConstElementPtr json; + json = isc::data::Element::fromJSON(configData->json_data_, true); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, "Configuration from database is expected to be " + "a map, i.e., start with { and end with } and contain " + "at least an entry called 'Dhcp6' that itself is a map. " + "The database contains a valid JSON, but its top element is not a map." + " Did you forget to add { } around your configuration?"); + } + + try { + isc::data::ConstElementPtr dhcp6; + // Get Dhcp6 component from the config + dhcp6 = json->get("Dhcp6"); + + if (!dhcp6) { + isc_throw(isc::BadValue, "no mandatory 'Dhcp6' entry in" + " the configuration"); + } + + // Replace the dhcp6 current config with the one read from database + db_config = boost::const_pointer_cast(dhcp6); + + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, + "Server Configuration error using using database: " + + std::string(ex.what()))); + } + + return (isc::config::createAnswer(0, + "Server Configuration successfully loaded from database.")); +} + void ControlledDhcpv6Srv::sessionReader(void) { // Process one asio event. If there are more events, iface_mgr will call // this callback more than once. @@ -941,5 +1192,5 @@ ControlledDhcpv6Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) { return(true); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index 31e738a91e..6e14eb51d6 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include namespace isc { namespace dhcp { @@ -120,6 +122,38 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { } private: + /// @brief Retrieves the server configuration information from the master database. + /// + /// Retrieves the server configuration information from the master database. + /// The information contains server specific settings and the shard credentials + /// where resides the rest of the configuration (common configuration for all servers) + /// + /// + /// @param dhcp6_config On input contains the current dhcp6 configuration in json format. + /// On output contains the new configuration read from database (also in json format) + /// + /// @return answer that contains result of the reconfiguration. + /// @throw Dhcp6ConfigError if trying to create a parser for NULL config. + static isc::data::ConstElementPtr + readDhcpv6SrvConfigFromMasterDatabase(isc::data::ElementPtr& db_local_config, + data::ElementPtr& db_logging, + SrvConfigMasterInfoPtr& configData); + + /// @brief Retrieves the server configuration information from database + /// and updates the current configuration. + /// + /// Retrieves the server configuration information from database + /// and replaces the current configuration received as paramater with the + /// new one. + /// + /// + /// @param db_config On input contains the current dhcp6 configuration in json format. + /// On output contains the new configuration read from database (also in json format) + /// + /// @return answer that contains result of the reconfiguration. + /// @throw Dhcp6ConfigError if trying to create a parser for NULL config. + static isc::data::ConstElementPtr + readDhcpv6SrvConfigFromDatabase(isc::data::ElementPtr& db_config); /// @brief Callback that will be called from iface_mgr when data /// is received over control socket. @@ -371,7 +405,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { TimerMgrPtr timer_mgr_; }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif diff --git a/src/bin/dhcp6/dhcp6_hooks.dox b/src/bin/dhcp6/dhcp6_hooks.dox index 8b76fd5a04..be89c09bea 100644 --- a/src/bin/dhcp6/dhcp6_hooks.dox +++ b/src/bin/dhcp6/dhcp6_hooks.dox @@ -327,7 +327,7 @@ to the end of this list. - name: @b response6, type: isc::dhcp::Pkt6Ptr, direction: in/out - @b Description: This callout is executed when server's response - is about to be send back to the client. The sole argument "response6" + is about to be sent back to the client. The sole argument "response6" contains a pointer to an @c isc::dhcp::Pkt6 object that contains the packet, with set source and destination addresses, interface over which it will be send, list of all options and relay information. All fields @@ -350,7 +350,7 @@ to the end of this list. - name: @b response6, type: isc::dhcp::Pkt6Ptr, direction: in/out - @b Description: This callout is executed when server's response is - assembled into binary form and is about to be send back to the + assembled into binary form and is about to be sent back to the client. The sole argument "response6" contains a pointer to an @c isc::dhcp::Pkt6 object that contains the packet, with set source and destination addresses, interface over which it will be sent, list of diff --git a/src/bin/dhcp6/dhcp6_lexer.ll b/src/bin/dhcp6/dhcp6_lexer.ll index a6194d8ea3..16ea120e72 100644 --- a/src/bin/dhcp6/dhcp6_lexer.ll +++ b/src/bin/dhcp6/dhcp6_lexer.ll @@ -36,7 +36,7 @@ unsigned int comment_start_line = 0; using namespace isc::dhcp; -}; +} /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::dhcp::Parser6Context::fatal(msg) @@ -439,6 +439,42 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } +\"configuration-type\" { + switch(driver.ctx_) { + case isc::dhcp::Parser6Context::DHCP6: + return isc::dhcp::Dhcp6Parser::make_CONFIGURATION_TYPE(driver.loc_); + default: + return isc::dhcp::Dhcp6Parser::make_STRING("configuration-type", driver.loc_); + } +} + +\"instance-id\" { + switch(driver.ctx_) { + case isc::dhcp::Parser6Context::DHCP6: + return isc::dhcp::Dhcp6Parser::make_INSTANCE_ID(driver.loc_); + default: + return isc::dhcp::Dhcp6Parser::make_STRING("instance-id", driver.loc_); + } +} + +\"master-database\" { + switch(driver.ctx_) { + case isc::dhcp::Parser6Context::DHCP6: + return isc::dhcp::Dhcp6Parser::make_MASTER_DATABASE(driver.loc_); + default: + return isc::dhcp::Dhcp6Parser::make_STRING("master-database", driver.loc_); + } +} + +\"config-database\" { + switch(driver.ctx_) { + case isc::dhcp::Parser6Context::DHCP6: + return isc::dhcp::Dhcp6Parser::make_CONFIG_DATABASE(driver.loc_); + default: + return isc::dhcp::Dhcp6Parser::make_STRING("config-database", driver.loc_); + } +} + \"re-detect\" { switch(driver.ctx_) { case isc::dhcp::Parser6Context::INTERFACES_CONFIG: @@ -486,6 +522,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"type\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: case isc::dhcp::Parser6Context::OPTION_DEF: @@ -534,6 +572,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"user\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_USER(driver.loc_); @@ -544,6 +584,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"password\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_PASSWORD(driver.loc_); @@ -554,6 +596,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"host\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_HOST(driver.loc_); @@ -564,6 +608,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"port\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_PORT(driver.loc_); @@ -574,6 +620,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"persist\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: case isc::dhcp::Parser6Context::SERVER_ID: @@ -585,6 +633,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"lfc-interval\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_LFC_INTERVAL(driver.loc_); @@ -593,28 +643,22 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } -\"connect-timeout\" { - switch(driver.ctx_) { - case isc::dhcp::Parser6Context::LEASE_DATABASE: - case isc::dhcp::Parser6Context::HOSTS_DATABASE: - return isc::dhcp::Dhcp6Parser::make_CONNECT_TIMEOUT(driver.loc_); - default: - return isc::dhcp::Dhcp6Parser::make_STRING("connect-timeout", driver.loc_); - } -} - -\"keyspace\" { +\"tcp-nodelay\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: - return isc::dhcp::Dhcp6Parser::make_KEYSPACE(driver.loc_); + return isc::dhcp::Dhcp6Parser::make_TCP_NODELAY(driver.loc_); default: - return isc::dhcp::Dhcp6Parser::make_STRING("keyspace", driver.loc_); + return isc::dhcp::Dhcp6Parser::make_STRING("tcp-nodelay", driver.loc_); } } \"reconnect-wait-time\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_RECONNECT_WAIT_TIME(driver.loc_); @@ -625,6 +669,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"request-timeout\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_REQUEST_TIMEOUT(driver.loc_); @@ -635,6 +681,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"tcp-keepalive\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_TCP_KEEPALIVE(driver.loc_); @@ -643,18 +691,34 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } -\"tcp-nodelay\" { +\"connect-timeout\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: - return isc::dhcp::Dhcp6Parser::make_TCP_NODELAY(driver.loc_); + return isc::dhcp::Dhcp6Parser::make_CONNECT_TIMEOUT(driver.loc_); default: - return isc::dhcp::Dhcp6Parser::make_STRING("tcp-nodelay", driver.loc_); + return isc::dhcp::Dhcp6Parser::make_STRING("connect-timeout", driver.loc_); + } +} + +\"keyspace\" { + switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: + case isc::dhcp::Parser6Context::LEASE_DATABASE: + case isc::dhcp::Parser6Context::HOSTS_DATABASE: + return isc::dhcp::Dhcp6Parser::make_KEYSPACE(driver.loc_); + default: + return isc::dhcp::Dhcp6Parser::make_STRING("keyspace", driver.loc_); } } \"contact-points\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_CONTACT_POINTS(driver.loc_); @@ -665,6 +729,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"max-reconnect-tries\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: return isc::dhcp::Dhcp6Parser::make_MAX_RECONNECT_TRIES(driver.loc_); @@ -771,6 +837,8 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} \"name\" { switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: case isc::dhcp::Parser6Context::LEASE_DATABASE: case isc::dhcp::Parser6Context::HOSTS_DATABASE: case isc::dhcp::Parser6Context::OPTION_DEF: @@ -1112,7 +1180,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } - \"debuglevel\" { switch(driver.ctx_) { case isc::dhcp::Parser6Context::LOGGERS: @@ -1326,7 +1393,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } - \"parameters\" { switch(driver.ctx_) { case isc::dhcp::Parser6Context::HOOKS_LIBRARIES: @@ -1552,7 +1618,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } - {JSONString} { /* A string has been matched. It contains the actual string and single quotes. We need to get those quotes out of the way and just use its content, e.g. @@ -1845,5 +1910,5 @@ class Dummy { /* cppcheck-suppress unusedPrivateFunction */ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); } }; -} +} // namespace #endif /* !__clang_analyzer__ */ diff --git a/src/bin/dhcp6/dhcp6_log.cc b/src/bin/dhcp6/dhcp6_log.cc index 98e319457e..d97d706551 100644 --- a/src/bin/dhcp6/dhcp6_log.cc +++ b/src/bin/dhcp6/dhcp6_log.cc @@ -14,6 +14,15 @@ namespace isc { namespace dhcp { +const int DBG_DHCP6_START = isc::log::DBGLVL_START_SHUT; +const int DBG_DHCP6_SHUT = isc::log::DBGLVL_START_SHUT; +const int DBG_DHCP6_COMMAND = isc::log::DBGLVL_COMMAND; +const int DBG_DHCP6_BASIC = isc::log::DBGLVL_TRACE_BASIC; +const int DBG_DHCP6_HOOKS = isc::log::DBGLVL_TRACE_BASIC; +const int DBG_DHCP6_BASIC_DATA = isc::log::DBGLVL_TRACE_BASIC_DATA; +const int DBG_DHCP6_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +const int DBG_DHCP6_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; + const char* DHCP6_ROOT_LOGGER_NAME = "kea-dhcp6"; const char* DHCP6_APP_LOGGER_NAME = "dhcp6"; const char* DHCP6_BAD_PACKET_LOGGER_NAME = "bad-packets"; @@ -29,6 +38,6 @@ isc::log::Logger options6_logger(DHCP6_OPTIONS_LOGGER_NAME); isc::log::Logger ddns6_logger(DHCP6_DDNS_LOGGER_NAME); isc::log::Logger lease6_logger(DHCP6_LEASE_LOGGER_NAME); -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/dhcp6_log.h b/src/bin/dhcp6/dhcp6_log.h index 32241318b3..33d1a30386 100644 --- a/src/bin/dhcp6/dhcp6_log.h +++ b/src/bin/dhcp6/dhcp6_log.h @@ -18,19 +18,19 @@ namespace dhcp { //@{ /// @brief Debug level used to log information during server startup. -const int DBG_DHCP6_START = isc::log::DBGLVL_START_SHUT; +extern const int DBG_DHCP6_START; /// @brief Debug level used to log information during server shutdown. -const int DBG_DHCP6_SHUT = isc::log::DBGLVL_START_SHUT; +extern const int DBG_DHCP6_SHUT; /// @brief Debug level used to log receiving commands. -const int DBG_DHCP6_COMMAND = isc::log::DBGLVL_COMMAND; +extern const int DBG_DHCP6_COMMAND; /// @brief Debug level used to trace basic operations within the code. -const int DBG_DHCP6_BASIC = isc::log::DBGLVL_TRACE_BASIC; +extern const int DBG_DHCP6_BASIC; /// @brief Debug level used to trace hook related operations -const int DBG_DHCP6_HOOKS = isc::log::DBGLVL_TRACE_BASIC; +extern const int DBG_DHCP6_HOOKS; /// @brief Debug level used to log the traces with some basic data. /// @@ -39,7 +39,7 @@ const int DBG_DHCP6_HOOKS = isc::log::DBGLVL_TRACE_BASIC; /// more detailed information in cases when it is warranted and the /// extraction of the data doesn't impact the server's performance /// significantly. -const int DBG_DHCP6_BASIC_DATA = isc::log::DBGLVL_TRACE_BASIC_DATA; +extern const int DBG_DHCP6_BASIC_DATA; /// @brief Debug level used to trace detailed errors. /// @@ -47,10 +47,10 @@ const int DBG_DHCP6_BASIC_DATA = isc::log::DBGLVL_TRACE_BASIC_DATA; /// packets. (These are not logged at severities of WARN or higher for fear /// that a set of deliberately invalid packets set to the server could overwhelm /// the logging.) -const int DBG_DHCP6_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +extern const int DBG_DHCP6_DETAIL; /// @brief This level is used to log the contents of packets received and sent. -const int DBG_DHCP6_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; +extern const int DBG_DHCP6_DETAIL_DATA; //@} @@ -101,7 +101,7 @@ extern isc::log::Logger packet6_logger; /// @brief Logger for options parser. /// /// This logger is used to issue log messages related to processing of the -/// DHCP options +/// DHCP options extern isc::log::Logger options6_logger; /// @brief Logger for Hostname or FQDN processing. @@ -117,7 +117,7 @@ extern isc::log::Logger lease6_logger; //@} -} // namespace dhcp6 -} // namespace isc +} // namespace dhcp6 +} // namespace isc #endif // DHCP6_LOG_H diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index aa6a6fe5fc..c4f3aa013f 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -827,3 +827,15 @@ such modification. The clients will remember previous server-id, and will use it to extend their leases. As a result, they will have to go through a rebinding phase to re-acquire their leases and associate them with a new server id. + +% DHCP6_TRANSACTION_FAILED transaction failed. error: %1 +The transaction failed with the error message. + +% DHCP6_FORCE_RELOAD_CONFIGURATION DHCPv6 force configuration reload. +A force configuration reload event has been initiated. + +% DHCP6_SHARD_CONFIGURATION_CHANGED DHCPv6 shard configuration is different. +A different shard configuration has been detected. + +% DHCP6_MASTER_CONFIGURATION_CHANGED DHCPv6 master configuration is different. +A different master configuration has been detected. diff --git a/src/bin/dhcp6/dhcp6_parser.yy b/src/bin/dhcp6/dhcp6_parser.yy index 32febb744a..3a9f60a178 100644 --- a/src/bin/dhcp6/dhcp6_parser.yy +++ b/src/bin/dhcp6/dhcp6_parser.yy @@ -54,6 +54,11 @@ using namespace std; INTERFACES "interfaces" RE_DETECT "re-detect" + CONFIGURATION_TYPE "configuration-type" + INSTANCE_ID "instance-id" + + MASTER_DATABASE "master-database" + CONFIG_DATABASE "config-database" LEASE_DATABASE "lease-database" HOSTS_DATABASE "hosts-database" HOSTS_DATABASES "hosts-databases" @@ -70,13 +75,13 @@ using namespace std; LFC_INTERVAL "lfc-interval" READONLY "readonly" CONNECT_TIMEOUT "connect-timeout" - CONTACT_POINTS "contact-points" - MAX_RECONNECT_TRIES "max-reconnect-tries" + TCP_NODELAY "tcp-nodelay" RECONNECT_WAIT_TIME "reconnect-wait-time" - KEYSPACE "keyspace" REQUEST_TIMEOUT "request-timeout" TCP_KEEPALIVE "tcp-keepalive" - TCP_NODELAY "tcp-nodelay" + CONTACT_POINTS "contact-points" + KEYSPACE "keyspace" + MAX_RECONNECT_TRIES "max-reconnect-tries" PREFERRED_LIFETIME "preferred-lifetime" VALID_LIFETIME "valid-lifetime" @@ -431,6 +436,8 @@ global_param: preferred_lifetime | subnet6_list | shared_networks | interfaces_config + | master_database + | config_database | lease_database | hosts_database | hosts_databases @@ -449,6 +456,8 @@ global_param: preferred_lifetime | user_context | comment | unknown_map_entry + | configuration_type + | instance_id ; preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER { @@ -517,12 +526,31 @@ interfaces_list: INTERFACES { ctx.leave(); }; +master_database: MASTER_DATABASE { + ElementPtr i(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("master-database", i); + ctx.stack_.push_back(i); + ctx.enter(ctx.MASTER_DATABASE); +} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + +config_database: CONFIG_DATABASE { + ElementPtr i(new MapElement(ctx.loc2pos(@1))); + ctx.stack_.back()->set("config-database", i); + ctx.stack_.push_back(i); + ctx.enter(ctx.CONFIG_DATABASE); +} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET { + ctx.stack_.pop_back(); + ctx.leave(); +}; + re_detect: RE_DETECT COLON BOOLEAN { ElementPtr b(new BoolElement($3, ctx.loc2pos(@3))); ctx.stack_.back()->set("re-detect", b); }; - lease_database: LEASE_DATABASE { ElementPtr i(new MapElement(ctx.loc2pos(@1))); ctx.stack_.back()->set("lease-database", i); @@ -589,12 +617,12 @@ database_map_param: database_type | lfc_interval | readonly | connect_timeout - | contact_points - | max_reconnect_tries + | tcp_nodelay | reconnect_wait_time | request_timeout | tcp_keepalive - | tcp_nodelay + | contact_points + | max_reconnect_tries | keyspace | unknown_map_entry ; @@ -669,6 +697,11 @@ connect_timeout: CONNECT_TIMEOUT COLON INTEGER { ctx.stack_.back()->set("connect-timeout", n); }; +tcp_nodelay: TCP_NODELAY COLON BOOLEAN { + ElementPtr n(new BoolElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("tcp-nodelay", n); +}; + reconnect_wait_time: RECONNECT_WAIT_TIME COLON INTEGER { ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); ctx.stack_.back()->set("reconnect-wait-time", n); @@ -684,11 +717,6 @@ tcp_keepalive: TCP_KEEPALIVE COLON INTEGER { ctx.stack_.back()->set("tcp-keepalive", n); }; -tcp_nodelay: TCP_NODELAY COLON BOOLEAN { - ElementPtr n(new BoolElement($3, ctx.loc2pos(@3))); - ctx.stack_.back()->set("tcp-nodelay", n); -}; - contact_points: CONTACT_POINTS { ctx.enter(ctx.NO_KEYWORD); } COLON STRING { @@ -697,11 +725,6 @@ contact_points: CONTACT_POINTS { ctx.leave(); }; -max_reconnect_tries: MAX_RECONNECT_TRIES COLON INTEGER { - ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); - ctx.stack_.back()->set("max-reconnect-tries", n); -}; - keyspace: KEYSPACE { ctx.enter(ctx.NO_KEYWORD); } COLON STRING { @@ -710,6 +733,10 @@ keyspace: KEYSPACE { ctx.leave(); }; +max_reconnect_tries: MAX_RECONNECT_TRIES COLON INTEGER { + ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("max-reconnect-tries", n); +}; mac_sources: MAC_SOURCES { ElementPtr l(new ListElement(ctx.loc2pos(@1))); @@ -780,6 +807,22 @@ relay_supplied_options: RELAY_SUPPLIED_OPTIONS { ctx.leave(); }; +configuration_type: CONFIGURATION_TYPE { + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr prf(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("configuration-type", prf); + ctx.leave(); +}; + +instance_id: INSTANCE_ID { + ctx.enter(ctx.NO_KEYWORD); +} COLON STRING { + ElementPtr prf(new StringElement($4, ctx.loc2pos(@4))); + ctx.stack_.back()->set("instance-id", prf); + ctx.leave(); +}; + hooks_libraries: HOOKS_LIBRARIES { ElementPtr l(new ListElement(ctx.loc2pos(@1))); ctx.stack_.back()->set("hooks-libraries", l); diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 30066b1b0e..b9fdfb0972 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -36,6 +36,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -44,7 +48,6 @@ #include #include #include - #include #include #include @@ -171,7 +174,7 @@ createStatusCode(const Pkt6& pkt, const Option6IA& ia, const uint16_t status_cod return (option_status); } -}; // anonymous namespace +} // namespace namespace isc { namespace dhcp { @@ -654,79 +657,168 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) { return; } - if (query->getType() == DHCPV6_DHCPV4_QUERY) { - // This call never throws. Should this change, this section must be - // enclosed in try-catch. - processDhcp4Query(query); - return; - } - - // Let's create a simplified client context here. AllocEngine::ClientContext6 ctx; - bool drop = false; - initContext(query, ctx, drop); - // Stop here if initContext decided to drop the packet. - if (drop) { - return; + bool transactionError = false; + bool reloadConfiguration = false; + try { + LeaseMgrFactory::instance().startTransaction(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp6_logger, DHCP6_TRANSACTION_FAILED).arg(ex.what()); + transactionError = true; } + if (!transactionError) { + bool rollback = false; + bool apply = true; + bool drop = false; + if (query->getType() != DHCPV6_DHCPV4_QUERY) { + initContext(query, ctx, drop); + // Stop here if initContext decided to drop the packet. + } - // Park point here. + // Park point here. - try { - switch (query->getType()) { - case DHCPV6_SOLICIT: - rsp = processSolicit(ctx); - break; + try { + if (!drop) { + switch (query->getType()) { + case DHCPV6_SOLICIT: + rsp = processSolicit(ctx); + break; - case DHCPV6_REQUEST: - rsp = processRequest(ctx); - break; + case DHCPV6_REQUEST: + rsp = processRequest(ctx); + break; - case DHCPV6_RENEW: - rsp = processRenew(ctx); - break; + case DHCPV6_RENEW: + rsp = processRenew(ctx); + break; - case DHCPV6_REBIND: - rsp = processRebind(ctx); - break; + case DHCPV6_REBIND: + rsp = processRebind(ctx); + break; - case DHCPV6_CONFIRM: - rsp = processConfirm(ctx); - break; + case DHCPV6_CONFIRM: + rsp = processConfirm(ctx); + break; - case DHCPV6_RELEASE: - rsp = processRelease(ctx); - break; + case DHCPV6_RELEASE: + rsp = processRelease(ctx); + break; - case DHCPV6_DECLINE: - rsp = processDecline(ctx); - break; + case DHCPV6_DECLINE: + rsp = processDecline(ctx); + break; - case DHCPV6_INFORMATION_REQUEST: - rsp = processInfRequest(ctx); - break; + case DHCPV6_INFORMATION_REQUEST: + rsp = processInfRequest(ctx); + break; - default: - return; - } + case DHCPV6_DHCPV4_QUERY: + processDhcp4Query(query); + break; - } catch (const std::exception& e) { + default: + // We received a packet type that we do not recognize. + LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_UNKNOWN_MSG_RECEIVED) + .arg(static_cast(query->getType())) + .arg(query->getIface()); + // Only action is to output a message if debug is enabled, + // and that will be covered by the debug statement before + // the "switch" statement. + ; + } + } + } catch (const TransactionException& e) { + LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL) + .arg(query->getName()) + .arg(query->getRemoteAddr().toText()) + .arg(e.what()); - // Catch-all exception (at least for ones based on the isc Exception - // class, which covers more or less all that are explicitly raised - // in the Kea code), but also the standard one, which may possibly be - // thrown from boost code. Just log the problem and ignore the packet. - // (The problem is logged as a debug message because debug is - // disabled by default - it prevents a DDOS attack based on the - // sending of problem packets.) - LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL) - .arg(query->getName()) - .arg(query->getRemoteAddr().toText()) - .arg(e.what()); + // Increase the statistic of dropped packets. + StatsMgr::instance().addValue("pkt6-receive-drop", static_cast(1)); + rollback = true; + apply = false; + } catch (const RFCViolation& e) { + LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL) + .arg(query->getName()) + .arg(query->getRemoteAddr().toText()) + .arg(e.what()); - // Increase the statistic of dropped packets. - StatsMgr::instance().addValue("pkt6-receive-drop", static_cast(1)); + // Increase the statistic of dropped packets. + StatsMgr::instance().addValue("pkt6-receive-drop", static_cast(1)); + rollback = true; + } catch (const std::exception& e) { + + // Catch-all exception (at least for ones based on the isc Exception + // class, which covers more or less all that are explicitly raised + // in the Kea code), but also the standard one, which may possibly be + // thrown from boost code. Just log the problem and ignore the packet. + // (The problem is logged as a debug message because debug is + // disabled by default - it prevents a DDOS attack based on the + // sending of problem packets.) + LOG_DEBUG(bad_packet6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL) + .arg(query->getName()) + .arg(query->getRemoteAddr().toText()) + .arg(e.what()); + + // Increase the statistic of dropped packets. + StatsMgr::instance().addValue("pkt6-receive-drop", static_cast(1)); + rollback = true; + } + if (rollback) { + if (apply) { + try { + LeaseMgrFactory::instance().rollback(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp6_logger, DHCP6_TRANSACTION_FAILED).arg(ex.what()); + } + } + } else { + try { + if (SrvConfigMgrFactory::initialized()) { + SrvConfigInfoPtr configInfo = + SrvConfigMgrFactory::instance().getConfig6Timestamp(); + if (configInfo && configInfo->timestamp_ != + CfgMgr::instance().getCurrentCfg()->getServerCfgTimestamp()) { + LOG_INFO(dhcp6_logger, DHCP6_SHARD_CONFIGURATION_CHANGED); + reloadConfiguration = true; + } + } + if (SrvConfigMasterMgrFactory::initialized()) { + std::string server_id = CfgMgr::instance().getCurrentCfg()->getInstanceId(); + SrvConfigMasterInfoPtr masterConfigInfo = + SrvConfigMasterMgrFactory::instance().getMasterConfig6Timestamp(server_id); + if (masterConfigInfo && masterConfigInfo->timestamp_ != + CfgMgr::instance().getCurrentCfg()->getMasterServerCfgTimestamp()) { + LOG_INFO(dhcp6_logger, DHCP6_MASTER_CONFIGURATION_CHANGED); + reloadConfiguration = true; + } + } + } catch (const std::exception& ex) { + LOG_INFO(dhcp6_logger, DHCP6_TRANSACTION_FAILED).arg(ex.what()); + reloadConfiguration = true; + } + if (!reloadConfiguration) { + try { + LeaseMgrFactory::instance().commit(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp6_logger, DHCP6_TRANSACTION_FAILED).arg(ex.what()); + reloadConfiguration = true; + } + } else { + try { + LeaseMgrFactory::instance().rollback(); + } catch (const std::exception& ex) { + LOG_INFO(dhcp6_logger, DHCP6_TRANSACTION_FAILED).arg(ex.what()); + } + } + } + } + + if (reloadConfiguration) { + LOG_INFO(dhcp6_logger, DHCP6_FORCE_RELOAD_CONFIGURATION); + kill(getpid(), SIGHUP); + rsp = Pkt6Ptr(); } if (!rsp) { @@ -1219,7 +1311,7 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt) { switch (pkt->getType()) { case DHCPV6_SOLICIT: case DHCPV6_REBIND: - case DHCPV6_CONFIRM: + case DHCPV6_CONFIRM: sanityCheck(pkt, MANDATORY, FORBIDDEN); return (true); @@ -1415,6 +1507,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer, if (answer_opt) { answer->addOption(answer_opt); } + break; } default: break; @@ -2140,7 +2233,6 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query, (*l)->preferred_lft_, (*l)->valid_lft_)); ia_rsp->addOption(prf); - if (pd_exclude_requested) { // PD exclude option has been requested via ORO, thus we need to // include it if the pool configuration specifies this option. @@ -2155,7 +2247,6 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query, } } - LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW) .arg(query->getLabel()) .arg((*l)->addr_.toText()) @@ -3749,5 +3840,5 @@ void Dhcpv6Srv::discardPackets() { HooksManager::clearParkingLots(); } -}; -}; +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 6debce96ac..df259594a3 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -28,6 +28,7 @@ #include #include #include +#include // Undefine the macro OPTIONAL which is defined in some operating // systems but conflicts with a member of the RequirementLevel enum in @@ -952,7 +953,7 @@ class Dhcpv6Srv : public Daemon { }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // DHCP6_SRV_H diff --git a/src/bin/dhcp6/dhcp6to4_ipc.cc b/src/bin/dhcp6/dhcp6to4_ipc.cc index 34873daaf1..ce8d12a1b7 100644 --- a/src/bin/dhcp6/dhcp6to4_ipc.cc +++ b/src/bin/dhcp6/dhcp6to4_ipc.cc @@ -58,7 +58,7 @@ void Dhcp6to4Ipc::handler() { try { LOG_DEBUG(packet6_logger, DBG_DHCP6_DETAIL, DHCP6_DHCP4O6_RECEIVING); - // Receive message from IPC. + // Receive message from IPC pkt = ipc.receive(); if (pkt) { @@ -142,12 +142,10 @@ void Dhcp6to4Ipc::handler() { // Update statistics accordingly for sent packet. Dhcpv6Srv::processStatsSent(pkt); - } catch (const std::exception& e) { LOG_ERROR(packet6_logger, DHCP6_DHCP4O6_SEND_FAIL).arg(e.what()); } } -}; // namespace dhcp - -}; // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 4d28b3781d..0939ae7011 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -323,7 +323,7 @@ class Dhcp6ConfigParser : public isc::data::SimpleParser { }; -} // anonymous namespace +} // namespace namespace isc { namespace dhcp { @@ -335,7 +335,7 @@ namespace dhcp { /// a command result, unless they specifically alter the channel configuration. /// In that case the user simply has to accept they'll be disconnected. /// -void configureCommandChannel() { +void configureCommandChannel6() { // Get new socket configuration. ConstElementPtr sock_cfg = CfgMgr::instance().getStagingCfg()->getControlSocketInfo(); @@ -555,6 +555,20 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, continue; } + if (config_pair.first == "master-database") { + DbAccessParser parser(DBType::MASTER_DB); + CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); + parser.parse(cfg_db_access, config_pair.second); + continue; + } + + if (config_pair.first == "config-database") { + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); + parser.parse(cfg_db_access, config_pair.second); + continue; + } + if (config_pair.first == "subnet6") { Subnets6ListConfigParser subnets_parser; // parse() returns number of subnets parsed. We may log it one day. @@ -643,11 +657,11 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, // configuration. This will add created subnets and option values into // the server's configuration. // This operation should be exception safe but let's make sure. - if (!rollback) { + if (!rollback && IfaceMgr::instance().isServerMode()) { try { // Setup the command channel. - configureCommandChannel(); + configureCommandChannel6(); // No need to commit interface names as this is handled by the // CfgMgr::commit() function. @@ -696,5 +710,81 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, return (answer); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +isc::data::ConstElementPtr +configureDhcp6ServerId(isc::data::ConstElementPtr config_set) { + ConstElementPtr answer; + + // Check if an 'instance-id' parameter is provided. + ConstElementPtr server_id = config_set->get("instance-id"); + if (server_id) { + std::string id; + if (server_id->getValue(id)) { + CfgMgr::instance().getStagingCfg()->getInstanceId() = id; + } + } + + // Everything was fine. Configuration is successful. + answer = isc::config::createAnswer(0, "Configuration successful."); + return (answer); +} + +isc::data::ConstElementPtr +configureDhcp6ServerConfigSource(isc::data::ConstElementPtr config_set) { + ConstElementPtr answer; + + SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + + // check if a 'configuration-type' parameter is provided + ConstElementPtr cfg_type_elem = config_set->get("configuration-type"); + if (cfg_type_elem) { + std::string type; + if (cfg_type_elem->getValue(type)) { + // Valid values for "configuration-type" : "database" and "file" + if (type == "database") { + // The configuration type specifies that the server configuration should be + // read from the database. + + // Parse the 'master-database' parameter in order to update the config manager + // with the configuration database type and credentials. + ConstElementPtr config_database = config_set->get("master-database"); + if (config_database) { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::MASTER_DATABASE; + + DbAccessParser parser(DBType::MASTER_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_database); + } else { + // There has not been specified a "master-database" parameter. + // Check if a "config-database" parameter has been specified + ConstElementPtr config_database = config_set->get("config-database"); + if (config_database) { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_database); + } + } + } else if (type == "file") { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::FILE; + } else { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::UNKNOWN; + + answer = isc::config::createAnswer(1, + "Invalid value for 'configuration-type' parameter, can't process config."); + return (answer); + } + } + } + + // Everything was fine. Configuration is successful. + answer = isc::config::createAnswer(0, "Configuration successful."); + return (answer); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/json_config_parser.h b/src/bin/dhcp6/json_config_parser.h index 3267eac4ba..e102d4b2bd 100644 --- a/src/bin/dhcp6/json_config_parser.h +++ b/src/bin/dhcp6/json_config_parser.h @@ -44,7 +44,34 @@ isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, bool check_only = false); -}; // end of isc::dhcp namespace -}; // end of isc namespace +/// @brief Configures the server identifier. +/// +/// This function is called every time a new configuration is received. +/// +/// @return answer that contains result of the reconfiguration. +isc::data::ConstElementPtr +configureDhcp6ServerId(data::ConstElementPtr config_set); + + +/// @brief Configures the location from where the server configuration should be +/// read +/// +/// This function is called every time a new configuration is received. +/// +/// This method does not throw. It catches all exceptions and returns them as +/// reconfiguration statuses. It may return the following response codes: +/// 0 - configuration successful +/// 1 - malformed configuration (parsing failed) +/// 2 - commit failed (parsing was successful, but the values could not be +/// stored in the configuration). +/// +/// @param config_set a new configuration for DHCPv6 server. +/// @return answer that contains result of the reconfiguration. +/// @throw Dhcp6ConfigError if trying to create a parser for NULL config. +isc::data::ConstElementPtr +configureDhcp6ServerConfigSource(data::ConstElementPtr config_set); + +} // namespace dhcp +} // namespace isc #endif // DHCP6_CONFIG_PARSER_H diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc index 6d848aa964..0538e58b4c 100644 --- a/src/bin/dhcp6/main.cc +++ b/src/bin/dhcp6/main.cc @@ -57,7 +57,7 @@ usage() { << "(useful for testing only)" << endl; exit(EXIT_FAILURE); } -} // end of anonymous namespace +} // namespace int main(int argc, char* argv[]) { diff --git a/src/bin/dhcp6/parser_context.cc b/src/bin/dhcp6/parser_context.cc index 0ed107886f..f76df94ed3 100644 --- a/src/bin/dhcp6/parser_context.cc +++ b/src/bin/dhcp6/parser_context.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/dhcp6/parser_context.h b/src/bin/dhcp6/parser_context.h index a3f7cc7101..f05072ea4e 100644 --- a/src/bin/dhcp6/parser_context.h +++ b/src/bin/dhcp6/parser_context.h @@ -218,12 +218,24 @@ class Parser6Context /// Used while parsing Dhcp6/interfaces structures. INTERFACES_CONFIG, + /// Used while parsing Dhcp6/master-database structures. + MASTER_DATABASE, + + /// Used while parsing Dhcp6/config-database structures. + CONFIG_DATABASE, + /// Used while parsing Dhcp6/lease-database structures. LEASE_DATABASE, /// Used while parsing Dhcp6/hosts-database[s] structures. HOSTS_DATABASE, + /// Used while parsing Dhcp6/configuration-type structures. + CONFIGURATION_TYPE, + + /// Used while parsing Dhcp6/instance-id structures. + INSTANCE_ID, + /// Used while parsing Dhcp6/*-database/type. DATABASE_TYPE, @@ -371,7 +383,7 @@ class Parser6Context isc::data::ElementPtr parseCommon(); }; -}; // end of isc::eval namespace -}; // end of isc namespace +} // namespace eval +} // namespace isc #endif diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 393d1bd4d7..e700470614 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -2687,4 +2687,4 @@ TEST_F(Dhcpv6SrvTest, truncatedVIVSO) { /// @todo: Implement proper tests for MySQL lease/host database, /// see ticket #4214. -} // end of anonymous namespace +} // namespace diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index b836b8fbc9..41226d5509 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -851,6 +851,6 @@ NakedDhcpv6SrvTest::checkIA_NAStatusCode( } } -}; // end of isc::dhcp::test namespace -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace test +} // namespace dhcp +} // namespace isc diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc index b36b97d680..c330b8ec6b 100644 --- a/src/bin/dhcp6/tests/hooks_unittest.cc +++ b/src/bin/dhcp6/tests/hooks_unittest.cc @@ -833,7 +833,7 @@ class HooksDhcpv6SrvTest : public Dhcpv6SrvTest { handle.getArgument("id_value", id_test); // Ok, now set the identifier. - std::vector id = { 0x66, 0x6f, 0x6f }; // foo + std::vector id = { 0x66, 0x6f, 0x6f }; // foo handle.setArgument("id_value", id); handle.setArgument("id_type", Host::IDENT_FLEX); @@ -4717,4 +4717,4 @@ TEST_F(LoadUnloadDhcpv6SrvTest, Dhcpv6SrvConfigured) { "3io_contextjson_confignetwork_stateserver_config")); } -} // end of anonymous namespace +} // namespace diff --git a/src/bin/dhcp6/tests/parser_unittest.cc b/src/bin/dhcp6/tests/parser_unittest.cc index a83e4a1b1a..65a7e3591a 100644 --- a/src/bin/dhcp6/tests/parser_unittest.cc +++ b/src/bin/dhcp6/tests/parser_unittest.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/bin/dhcp6/tests/rebind_unittest.cc b/src/bin/dhcp6/tests/rebind_unittest.cc index 207db8e0f1..f2c146b7f4 100644 --- a/src/bin/dhcp6/tests/rebind_unittest.cc +++ b/src/bin/dhcp6/tests/rebind_unittest.cc @@ -1144,5 +1144,4 @@ TEST_F(RebindTest, optionsInheritance) { ASSERT_TRUE(client.hasOptionWithAddress(D6O_SNTP_SERVERS, "3000:2::2")); } - -} // end of anonymous namespace +} // namespace diff --git a/src/bin/dhcp6/tests/shared_network_unittest.cc b/src/bin/dhcp6/tests/shared_network_unittest.cc index 7ffe9fc24d..8f46ff6b83 100644 --- a/src/bin/dhcp6/tests/shared_network_unittest.cc +++ b/src/bin/dhcp6/tests/shared_network_unittest.cc @@ -427,7 +427,7 @@ const char* NETWORKS_CONFIG[] = { " }" " ]," " \"subnet6\": [" - " \{" + " {" " \"subnet\": \"3000::/96\"," " \"id\": 1000," " \"interface\": \"eth0\"," diff --git a/src/bin/kea_config_tool/.gitignore b/src/bin/kea_config_tool/.gitignore new file mode 100644 index 0000000000..a8d4a3e6af --- /dev/null +++ b/src/bin/kea_config_tool/.gitignore @@ -0,0 +1,5 @@ +/kea-config-tool +/config_tool_messages.cc +/config_tool_messages.h +/s-messages +/kea-config-tool.8 diff --git a/src/bin/kea_config_tool/Makefile.am b/src/bin/kea_config_tool/Makefile.am new file mode 100644 index 0000000000..931e0cc61e --- /dev/null +++ b/src/bin/kea_config_tool/Makefile.am @@ -0,0 +1,79 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +AM_LDFLAGS = -lm +if USE_STATIC_LINK +AM_LDFLAGS += -static +endif + +config_tool_messages.h config_tool_messages.cc: s-messages + +s-messages: config_tool_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/bin/kea_config_tool/config_tool_messages.mes + touch $@ + +BUILT_SOURCES = config_tool_messages.h config_tool_messages.cc + +# convenience archive + +noinst_LTLIBRARIES = libkeaconfigtool.la + +libkeaconfigtool_la_SOURCES = +libkeaconfigtool_la_SOURCES += command_options.cc command_options.h +libkeaconfigtool_la_SOURCES += config_tool_controller.cc config_tool_controller.h +libkeaconfigtool_la_SOURCES += server_config.cc server_config.h +libkeaconfigtool_la_SOURCES += config_tool_log.cc config_tool_log.h +libkeaconfigtool_la_SOURCES += kea_admin.cc kea_admin.h + +nodist_libkeaconfigtool_la_SOURCES = config_tool_messages.cc config_tool_messages.h +EXTRA_DIST = config_tool_messages.mes + +sbin_PROGRAMS = kea-config-tool +kea_config_tool_SOURCES = main.cc +libkea_keaconfigtool___la_CXXFLAGS = $(AM_CXXFLAGS) + +kea_config_tool_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +kea_config_tool_LDADD = libkeaconfigtool.la +kea_config_tool_LDADD += $(top_builddir)/src/bin/dhcp4/libdhcp4.la +kea_config_tool_LDADD += $(top_builddir)/src/bin/dhcp6/libdhcp6.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +kea_config_tool_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +kea_config_tool_LDADD += $(CRYPTO_LIBS) +kea_config_tool_LDADD += $(BOOST_LIBS) +kea_config_tool_LDADD += $(PTHREAD_LDFLAGS) + +CLEANFILES = config_tool_messages.h config_tool_messages.cc s-messages + +man_MANS = kea-config-tool.8 +DISTCLEANFILES = $(man_MANS) +EXTRA_DIST += $(man_MANS) kea-config-tool.xml + +if GENERATE_DOCS + +kea-config-tool.8: kea-config-tool.xml + @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/kea-config-tool.xml + +else + +$(man_MANS): + @echo Man generation disabled. Creating dummy $@. Configure with --enable-generate-docs to enable it. + @echo Man generation disabled. Remove this file, configure with --enable-generate-docs, and rebuild Kea > $@ + +endif diff --git a/src/bin/kea_config_tool/command_options.cc b/src/bin/kea_config_tool/command_options.cc new file mode 100644 index 0000000000..e12dd73ffb --- /dev/null +++ b/src/bin/kea_config_tool/command_options.cc @@ -0,0 +1,389 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include +#include +#include + +#include + +namespace isc { +namespace config_tool { + +CommandOptions::CommandOptions() { + resetInternalData(); +} + +CommandOptions::~CommandOptions() { +} + +CommandOptions& CommandOptions::instance() { + static CommandOptions options; + return (options); +} + +void CommandOptions::usage() { + switch (show_help_type_) { + case HT_COMMANDS: + usageCommands(); + break; + case HT_SHARD_CONFIG: + usageCmdShardConfig(); + break; + case HT_MASTER_CONFIG: + usageCmdMasterConfig(); + break; + default: + isc_throw(InvalidUsage, "unknown help type: " << static_cast(show_help_type_)); + } +} + +void CommandOptions::usageCommands() { + std::cout << ConfigToolController::config_tool_bin_name_ + << " [options...] [ [args...]]" << std::endl + << std::endl; + + std::cout << " Options:" << std::endl + << "\t -v: print version number and exit" << std::endl + << "\t -V: print detailed version number and exit" << std::endl + << "\t -d: debug mode with extra verbose data" << std::endl + << "\t -h: print this help" << std::endl + << std::endl; + + std::cout << " Commands ('command name' -h for help):" << std::endl + << "\t -dbinit: Database initialization. " << std::endl + << "\t -shard: Operations with shards databases." << std::endl + << "\t -master: Operation with master databases." << std::endl; +} + +void CommandOptions::usageCmdShardConfig() { + std::cout << " NAME " << std::endl + << "\t -shard: Configures a shard database" << std::endl + << std::endl; + + std::cout << " SYNOPSIS " << std::endl + << "\t -shard -set-config -4|-6 master-config-file " + "input-shards-directory-path [list-of-shards]" + << std::endl + << "\t -shard -get-config -4|-6 master-config-file " + "output-shards-directory-path list-of-shards" + << std::endl + << std::endl; + + std::cout << " DESCRIPTION" << std::endl + << "\t -set-config writes the provided server configuration " + "into shards." + << std::endl + << "\t -get-config retrieves server configuration from shards " + "and writes it locally." + << std::endl + << std::endl; + + std::cout << " OPTIONS" << std::endl + << "\t -4|-6 - specifies DHCPV6 or DHCPV4 operation." << std::endl + << "\t master-config-file - configuration file which provides the " + "master database backend credentials." + << std::endl + << "\t input-shards-directory-path - The path from the " + "shards configuration is loaded." + << std::endl + << "\t output-shards-directory-path - The path where the " + "shards configuration will be stored." + << std::endl + << "\t list-of-shards - the shards names (comma separated) for " + "which the command is applied" + << std::endl + << "\t\t\t (for the case when list-of-shards is optional and no " + "value " + << std::endl + << "\t\t\t is provided then the command is applied for all found " + "shards)." + << std::endl; +} + +void CommandOptions::usageCmdMasterConfig() { + std::cout << " NAME " << std::endl + << "\t -master - Configures a master database" << std::endl + << std::endl; + + std::cout << " SYNOPSIS " << std::endl + << "\t -master -set-servers -4|-6 master-config-file " + "input-shards-directory-path [list-of-shards]" + << std::endl + << "\t -master -get-servers -4|-6 master-config-file " + "output-shards-directory-path" + << std::endl + << std::endl; + + std::cout << " DESCRIPTION" << std::endl + << "\t -set-servers write the provided servers configuration " + "into master database." + << std::endl + << "\t -get-servers retrieves servers configuration from the " + "master database and writes them locally." + << std::endl + << std::endl; + + std::cout << " OPTIONS" << std::endl + << "\t -4|-6 - specifies DHCPV6 or DHCPV4 operation." << std::endl + << "\t master-config-file - configuration file which provides the " + "master database backend credentials." + << std::endl + << "\t input-shards-directory-path - The path from the " + "master configuration is loaded." + << std::endl + << "\t output-shards-directory-path - The path where the " + "master configuration will be stored." + << std::endl + << "\t list-of-shards - the shards names (comma separated) for " + "which the command is applied" + << std::endl + << "\t\t\t (for the case when list-of-shards is optional and not " + "specified" + << std::endl + << "\t\t\t is provided then the command is applied for all found " + "shards)." + << std::endl; +} + +void CommandOptions::parse(int argc, char* argv[]) { + resetInternalData(); + + if (argc == 1) { + isc_throw_1(InvalidUsage, "missing command", true); + return; + } + + int paramIdx = 1; + + std::string param; + while (getParameter(argc, argv, paramIdx++, param)) { + if (param.compare("-h") == 0) { + show_help_ = true; + show_help_type_ = HT_COMMANDS; + } else if (param.compare("-d") == 0) { + verbose_mode_ = true; + } else if (param.compare("-v") == 0) { + show_version_ = true; + show_detailed_version_ = false; + } else if (param.compare("-V") == 0) { + show_version_ = true; + show_detailed_version_ = true; + } else if (param.compare("-dbinit") == 0) { + parseDatabaseInitCmdLine(argc, argv, paramIdx); + } else if (param.compare("-shard") == 0) { + parseShardConfigCmdLine(argc, argv, paramIdx); + } else if (param.compare("-master") == 0) { + parseMasterConfigCmdLine(argc, argv, paramIdx); + } else { + isc_throw(InvalidUsage, "unknown parameter '" << param << "'"); + } + } +} + +void CommandOptions::resetInternalData() { + dhcp_space_type_ = DHCP_SPACE_UNKNOWN; + run_admin_script_ = false; + admin_script_params_ = ""; + verbose_mode_ = false; + input_db_credentials_file_ = ""; + config_path_ = ""; + shard_set_config_ = false; + shard_get_config_ = false; + master_set_servers_ = false; + master_get_servers_ = false; + show_help_ = false; + show_version_ = false; + show_detailed_version_ = false; + show_help_type_ = HT_COMMANDS; + items_list_.clear(); +} + +void CommandOptions::parseShardConfigCmdLine(int argc, char* argv[], int& paramIdx) { + std::string param; + while (getParameter(argc, argv, paramIdx++, param)) { + if (param.compare("-h") == 0) { + show_help_ = true; + show_help_type_ = HT_SHARD_CONFIG; + } else if (param.compare("-get-config") == 0) { + shard_get_config_ = true; + std::string param; + if (!getParameter(argc, argv, paramIdx++, param)) { + isc_throw(InvalidUsage, "missing DHCP space parameter"); + } + if (param.compare("-4") == 0) { + dhcp_space_type_ = DHCP_SPACE_V4; + } else if (param.compare("-6") == 0) { + dhcp_space_type_ = DHCP_SPACE_V6; + } else { + isc_throw(InvalidUsage, "unknown parameter '" << param << "'"); + } + if (!getParameter(argc, argv, paramIdx++, input_db_credentials_file_)) { + isc_throw(InvalidUsage, "missing parameter 'input_db_credentials_file'"); + } + if (!getParameter(argc, argv, paramIdx++, config_path_)) { + isc_throw(InvalidUsage, "missing parameter 'output_config_path'"); + } + std::string shards_list; + if (!getParameter(argc, argv, paramIdx++, shards_list)) { + isc_throw(InvalidUsage, "missing parameter 'list_of_shards'"); + } + boost::char_separator sep(","); + boost::tokenizer> tokens(shards_list, sep); + for (const std::string& shard : tokens) { + if (!shard.empty()) { + items_list_.insert(shard); + } + } + } else if (param.compare("-set-config") == 0) { + shard_set_config_ = true; + std::string param; + if (!getParameter(argc, argv, paramIdx++, param)) { + isc_throw(InvalidUsage, "missing DHCP space parameter"); + } + if (param.compare("-4") == 0) { + dhcp_space_type_ = DHCP_SPACE_V4; + } else if (param.compare("-6") == 0) { + dhcp_space_type_ = DHCP_SPACE_V6; + } else { + isc_throw(InvalidUsage, "missing DHCP space parameter"); + } + if (!getParameter(argc, argv, paramIdx++, input_db_credentials_file_)) { + isc_throw(InvalidUsage, "missing parameter 'master-config-file'"); + } + if (!getParameter(argc, argv, paramIdx++, config_path_)) { + isc_throw(InvalidUsage, "missing parameter 'shards-directory-path'"); + } + std::string shards_list; + if (!getParameter(argc, argv, paramIdx++, shards_list)) { + return; + } + boost::char_separator sep(","); + boost::tokenizer> tokens(shards_list, sep); + for (const std::string& shard : tokens) { + if (!shard.empty()) { + items_list_.insert(shard); + } + } + } else { + isc_throw(InvalidUsage, "unknown parameter '" << param << "'"); + } + } +} + +void CommandOptions::parseMasterConfigCmdLine(int argc, char* argv[], int& paramIdx) { + std::string param; + while (getParameter(argc, argv, paramIdx++, param)) { + if (param.compare("-h") == 0) { + show_help_ = true; + show_help_type_ = HT_MASTER_CONFIG; + } else if (param.compare("-set-servers") == 0) { + master_set_servers_ = true; + std::string param; + if (!getParameter(argc, argv, paramIdx++, param)) { + isc_throw(InvalidUsage, "missing DHCP space parameter"); + } + if (param.compare("-4") == 0) { + dhcp_space_type_ = DHCP_SPACE_V4; + } else if (param.compare("-6") == 0) { + dhcp_space_type_ = DHCP_SPACE_V6; + } else { + isc_throw(InvalidUsage, "unknown parameter '" << param << "'"); + } + if (!getParameter(argc, argv, paramIdx++, input_db_credentials_file_)) { + isc_throw(InvalidUsage, "missing parameter 'input_db_credentials_file'"); + } + if (!getParameter(argc, argv, paramIdx++, config_path_)) { + isc_throw(InvalidUsage, "missing parameter 'input_config_path'"); + } + + std::string shards_list; + if (!getParameter(argc, argv, paramIdx++, shards_list)) { + return; + } + boost::char_separator sep(","); + boost::tokenizer> tokens(shards_list, sep); + for (const std::string& shard : tokens) { + if (!shard.empty()) { + items_list_.insert(shard); + } + } + } else if (param.compare("-get-servers") == 0) { + master_get_servers_ = true; + std::string param; + if (!getParameter(argc, argv, paramIdx++, param)) { + isc_throw(InvalidUsage, "missing DHCP space parameter"); + } + if (param.compare("-4") == 0) { + dhcp_space_type_ = DHCP_SPACE_V4; + } else if (param.compare("-6") == 0) { + dhcp_space_type_ = DHCP_SPACE_V6; + } else { + isc_throw(InvalidUsage, "unknown parameter '" << param << "'"); + } + if (!getParameter(argc, argv, paramIdx++, input_db_credentials_file_)) { + isc_throw(InvalidUsage, "missing parameter 'input_db_credentials_file'"); + } + if (!getParameter(argc, argv, paramIdx++, config_path_)) { + isc_throw(InvalidUsage, "missing parameter 'output_config_path'"); + } + } else { + isc_throw(InvalidUsage, "unknown parameter '" << param << "'"); + } + } +} + +void CommandOptions::parseDatabaseInitCmdLine(int argc, char* argv[], int& paramIdx) { + run_admin_script_ = true; + // There is no need to throw exception if the function returns false + // because admin script parameters may miss. + getUnparsedCommandLine(argc, argv, paramIdx, admin_script_params_); + // All parameters are sent to kea-admin script, so there are + // no more parameters to parse + paramIdx = argc; +} + +bool CommandOptions::getParameter(int argc, char* argv[], int paramIdx, std::string& paramValue) { + if (paramIdx >= argc) { + return false; + } + paramValue = std::string(argv[paramIdx]); + return true; +} + +bool CommandOptions::getUnparsedCommandLine(int argc, + char* argv[], + int paramIdx, + std::string& unparsed) { + if (paramIdx >= argc) { + return false; + } + unparsed = ""; + for (int nIdx = paramIdx; nIdx < argc; nIdx++) { + if (nIdx > paramIdx) { + unparsed += ' '; + } + unparsed += std::string(argv[nIdx]); + } + return true; +} + +} // namespace config_tool +} // namespace isc diff --git a/src/bin/kea_config_tool/command_options.h b/src/bin/kea_config_tool/command_options.h new file mode 100644 index 0000000000..bd7a7701bc --- /dev/null +++ b/src/bin/kea_config_tool/command_options.h @@ -0,0 +1,130 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMAND_OPTIONS_H +#define COMMAND_OPTIONS_H + +#include +#include + +#include +#include + +namespace isc { +namespace config_tool { + +/// \brief Command Options. +/// +/// This class is responsible for parsing the command-line and storing the +/// specified options. +/// +class CommandOptions : public boost::noncopyable { +public: + enum HelpType { HT_COMMANDS, HT_SHARD_CONFIG, HT_MASTER_CONFIG }; + + CommandOptions(); + ~CommandOptions(); + + /// CommandOptions is a singleton class. This method returns reference + /// to its sole instance. + /// + /// \return the only existing instance of command options + static CommandOptions& instance(); + + /// @brief Prints Kea Config Tool Usage + /// + void usage(); + void usageCommands(); + void usageCmdShardConfig(); + void usageCmdMasterConfig(); + + void parse(int argc, char* argv[]); + + bool Param_ShowHelp() const { + return show_help_; + } + bool Param_ShowVersion() const { + return show_version_; + } + bool Param_ShowDetailedVersion() const { + return show_detailed_version_; + } + bool Param_VerboseMode() const { + return verbose_mode_; + } + bool Param_RunAdminScript() const { + return run_admin_script_; + } + std::string Param_AdminScriptParams() const { + return admin_script_params_; + } + std::string Param_InputDbCredentialsFile() const { + return input_db_credentials_file_; + } + std::string Param_ConfigPath() const { + return config_path_; + } + bool Param_ShardSetConfig() const { + return shard_set_config_; + } + bool Param_ShardGetConfig() const { + return shard_get_config_; + } + bool Param_MasterSetServers() const { + return master_set_servers_; + } + bool Param_MasterGetServers() const { + return master_get_servers_; + } + DhcpSpaceType Param_DhcpSpaceType() const { + return dhcp_space_type_; + } + HelpType Param_ShowHelpType() const { + return show_help_type_; + } + const std::set& Param_ItemsList() const { + return items_list_; + } + void resetInternalData(); + +protected: + void parseShardConfigCmdLine(int argc, char* argv[], int& paramIdx); + void parseMasterConfigCmdLine(int argc, char* argv[], int& paramIdx); + void parseDatabaseInitCmdLine(int argc, char* argv[], int& paramIdx); + bool getParameter(int argc, char* argv[], int paramIdx, std::string& paramValue); + bool getUnparsedCommandLine(int argc, char* argv[], int paramIdx, std::string& remainedLine); + + bool show_help_; + bool show_version_; + bool show_detailed_version_; + bool verbose_mode_; + bool run_admin_script_; + std::string admin_script_params_; + std::string input_db_credentials_file_; + std::string config_path_; + std::set items_list_; + bool shard_set_config_; + bool shard_get_config_; + bool master_set_servers_; + bool master_get_servers_; + DhcpSpaceType dhcp_space_type_; + HelpType show_help_type_; +}; + +} // namespace config_tool +} // namespace isc + +#endif // COMMAND_OPTIONS_H diff --git a/src/bin/kea_config_tool/config-tool.xml b/src/bin/kea_config_tool/config-tool.xml new file mode 100644 index 0000000000..faa4d8ba8e --- /dev/null +++ b/src/bin/kea_config_tool/config-tool.xml @@ -0,0 +1,193 @@ +]> + + + + + July 3, 2014 + + + + keactrl + 8 + Kea + + + + keactrl + Shell script for managing Kea + + + + + 2014 + Internet Systems Consortium, Inc. ("ISC") + + + + + + keactrl + command + + + + + + + DESCRIPTION + + keactrl is a shell script which controls the startup, shutdown + and reconfiguration of the Kea servers (kea-dhcp4, + kea-dhcp6 and kea-dhcp-ddns). It + also provides the means for checking the current status of the servers + and determining the configuration files in use. + + + + + CONFIGURATION FILE + + Depending on requirements, not all of the available servers need + be run. The keactrl configuration file sets which servers are + enabled and which are disabled. By default the configuration + file is [kea-install-dir]/etc/kea/keactrl.conf. + + + See the Kea Administrator's Guide for the documentation of the parameters + in the keactrl configuration file. + + + + + OPTIONS + + + + + + Command to be issued to the servers. It can be one of the following: + + + + + start + + Start the servers. + + + + + stop + + Stop the servers. + + + + + reload + + Instructs the servers to re-read the kea configuration file. + + + + + status + + Print the status of the servers. + + + + + + + + + + + + + + Specify the keactrl configuration file. Without + this switch, the keactrl will attempt to use the + file [kea-install-dir]/etc/kea/keactrl.conf. + + + + + + + + + Specifies a subset of the enabled servers to which the command should be issued. + The list of servers should be separated by commas with no intervening spaces. + Acceptable values are: + + + + + dhcp4 + + DHCPv4 server (kea-dhcp4). + + + + + dhcp6 + + DHCPv6 server (kea-dhcp6). + + + + + dhcp_ddns + + DHCP DDNS server (kea-dhcp-ddns). + + + + + all + + All servers (default). + + + + + + + + + + + + SEE ALSO + + + kea-dhcp4 + 8 + , + + + kea-dhcp6 + 8 + , + + + kea-dhcp-ddns + 8 + , + + Kea Administrator's Guide. + + + + + diff --git a/src/bin/kea_config_tool/config_tool_controller.cc b/src/bin/kea_config_tool/config_tool_controller.cc new file mode 100644 index 0000000000..779d94c247 --- /dev/null +++ b/src/bin/kea_config_tool/config_tool_controller.cc @@ -0,0 +1,1204 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace isc::dhcp; +using namespace isc::log; + +namespace isc { +namespace config_tool { + +/// @brief Defines the application name, it may be used to locate +/// configuration data and appears in log statements. +constexpr char ConfigToolController::config_tool_app_name_[] = "KeaConfigTool"; + +/// @brief Defines the executable name. +constexpr char ConfigToolController::config_tool_bin_name_[] = "kea-config-tool"; + +ConfigToolController::ConfigToolController() { +} + +ConfigToolController::~ConfigToolController() { +} + +ConfigToolController& ConfigToolController::instance() { + static ConfigToolController config_tool; + return (config_tool); +} + +void ConfigToolController::launch(int argc, char* argv[]) { + CommandOptions& cmdOptions = CommandOptions::instance(); + + try { + startLogger(); + + cmdOptions.parse(argc, argv); + + if (cmdOptions.Param_ShowHelp()) { + switch (cmdOptions.Param_ShowHelpType()) { + case CommandOptions::HT_COMMANDS: + cmdOptions.usage(); + break; + case CommandOptions::HT_SHARD_CONFIG: + cmdOptions.usageCmdShardConfig(); + break; + case CommandOptions::HT_MASTER_CONFIG: + cmdOptions.usageCmdMasterConfig(); + break; + } + + return; + } + + if (cmdOptions.Param_ShowVersion()) { + showApplicationVersion(cmdOptions.Param_ShowDetailedVersion()); + return; + } + + if (cmdOptions.Param_ShardSetConfig()) { + setShardsConfig(cmdOptions.Param_DhcpSpaceType(), + cmdOptions.Param_InputDbCredentialsFile(), + cmdOptions.Param_ConfigPath(), // + cmdOptions.Param_ItemsList()); + std::cout << "The operation has completed successfully." << std::endl; + return; + } + + if (cmdOptions.Param_ShardGetConfig()) { + getShardsConfig(cmdOptions.Param_DhcpSpaceType(), + cmdOptions.Param_InputDbCredentialsFile(), + cmdOptions.Param_ConfigPath(), // + cmdOptions.Param_ItemsList()); + std::cout << "The operation has completed successfully." << std::endl; + return; + } + + if (cmdOptions.Param_MasterSetServers()) { + masterSetServers(cmdOptions.Param_DhcpSpaceType(), + cmdOptions.Param_InputDbCredentialsFile(), + cmdOptions.Param_ConfigPath(), // + cmdOptions.Param_ItemsList()); + std::cout << "The operation has completed successfully." << std::endl; + return; + } + + if (cmdOptions.Param_MasterGetServers()) { + masterGetServers(cmdOptions.Param_DhcpSpaceType(), + cmdOptions.Param_InputDbCredentialsFile(), + cmdOptions.Param_ConfigPath()); + std::cout << "The operation has completed successfully." << std::endl; + return; + } + + if (cmdOptions.Param_RunAdminScript()) { + KeaAdmin::runAdminScript(cmdOptions.Param_AdminScriptParams()); + std::cout << "The operation has completed successfully." << std::endl; + return; + } + } catch (const InvalidUsage& ex) { + if (ex.getShowAppHelp()) { + cmdOptions.usage(); + } + throw; + } +} + +void ConfigToolController::startLogger() const { + OutputOption option; + LoggerManager manager; + + initLogger(config_tool_app_name_, DEBUG, 99, NULL, false); + + // Prepare the objects to define the logging specification + LoggerSpecification spec(getRootLoggerName(), keaLoggerSeverity(DEBUG), keaLoggerDbglevel(99)); + + // If we are running in verbose (debugging) mode + // we send the output to the console, otherwise + // by default we send it to the SYSLOG + + if (CommandOptions::instance().Param_VerboseMode()) { + option.destination = OutputOption::DEST_CONSOLE; + } else { + option.destination = OutputOption::DEST_SYSLOG; + } + + // ... and set the destination + spec.addOutputOption(option); + + manager.process(spec); +} + +std::string ConfigToolController::getVersion(bool extended) { + std::stringstream tmp; + + tmp << VERSION; + if (extended) { + tmp << std::endl << EXTENDED_VERSION << std::endl; + } + + return (tmp.str()); +} + +void ConfigToolController::showApplicationVersion(bool show_detailed_version) { + std::cout << ConfigToolController::getVersion(show_detailed_version) << std::endl; +} + +void ConfigToolController::setShardsConfig(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& input_shards_directory_path, + const std::set& optional_shards_list) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_UPDATE_SHARDS_CONFIG); + + if (dhcp_space == DHCP_SPACE_UNKNOWN) { + isc_throw(InvalidUsage, "no DHCP space provided. Use the -4|-6 option"); + } + + if (master_config_file.empty() || input_shards_directory_path.empty()) { + isc_throw(InvalidUsage, "neither the master database config file nor the input config" + "directory have been specified"); + } + + std::vector shard_directories; + populateDirectories(input_shards_directory_path, true, false, shard_directories); + + std::string credentials_config; + isc::data::ConstElementPtr dhcp_config = + ConfigToolController::configureConfigToolConfigSource(dhcp_space, master_config_file); + SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + if (!ConfigToolController::configureConfigToolId(dhcp_config)) { + isc_throw(RunTimeFail, "config tool id not set in configuration as" + " mandatory 'instance-id' parameter is missing"); + } + + CfgDbAccessPtr cfg_db = srv_cfg->getCfgDbAccess(); + if (dhcp_space == DHCP_SPACE_V6) { + cfg_db->setAppendedParameters("universe=6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + cfg_db->setAppendedParameters("universe=4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + cfg_db->createSrvMasterCfgManagers(); + } else if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::CONFIG_DATABASE) { + // Get 'config-database' component from the config. + auto config_database = dhcp_config->get("config-database"); + if (!config_database) { + isc_throw(isc::BadValue, "no mandatory 'config-database' entry in" + " the configuration"); + } + credentials_config = config_database->str(); + std::string database_name_key = "name"; + auto database_name_node = config_database->get(database_name_key); + if (!database_name_node) { + database_name_key = "keyspace"; + database_name_node = config_database->get(database_name_key); + } + if (!database_name_node) { + isc_throw(isc::BadValue, "neither 'name' nor 'keyspace' entry in 'config-database'"); + } + if (database_name_node->getType() != isc::data::Element::string) { + isc_throw(isc::BadValue, "'" + database_name_key + "' is not of type string"); + } + std::string shard_database = database_name_node->stringValue(); + std::cout << "Searching credentials for " << shard_database << std::endl; + std::vector filtered_shard_directories; + for (int idxShard = 0; idxShard < shard_directories.size(); idxShard++) { + std::string shard_name = shard_directories[idxShard].name; + if (shard_database == shard_name) { + std::cout << "Detected credentials for " << shard_name << std::endl; + filtered_shard_directories.push_back(shard_directories[idxShard]); + } else { + std::cout << "Unknown credentials for shard " << shard_name << std::endl; + } + } + shard_directories = filtered_shard_directories; + } else { + isc_throw(RunTimeFail, "unknown configuration type"); + } + + if (shard_directories.size() == 0) { + isc_throw(RunTimeFail, "there are no shards in the provided directory to match" + " used credentials"); + } + + for (int idxShard = 0; idxShard < shard_directories.size(); idxShard++) { + std::string shard_name = shard_directories[idxShard].name; + std::string shard_cfg_path = shard_directories[idxShard].full_path; + + if (!optional_shards_list.empty()) { + std::set::const_iterator it = optional_shards_list.find(shard_name); + if (it == optional_shards_list.end()) { + continue; + } + } + + std::string credentials_cfg_file = shard_cfg_path + "/credentials.json"; + std::string json_cfg_file = shard_cfg_path + "/config.json"; + std::string generic_cfg_file = shard_cfg_path + "/config.generic"; + std::string timestamp_file = shard_cfg_path + "/config.timestamp"; + + if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + parseMasterCredentialsJson(shard_name, credentials_cfg_file, credentials_config); + } + + updateShardConfig(dhcp_space, shard_name, credentials_config, json_cfg_file, + generic_cfg_file, timestamp_file); + } +} + +void ConfigToolController::getShardsConfig(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& output_shards_directory_path, + const std::set& shards_list) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_GET_SHARDS_CONFIG); + + if (dhcp_space == DHCP_SPACE_UNKNOWN) { + isc_throw(InvalidUsage, "no DHCP space provided. Use the -4|-6 option"); + } + + if (master_config_file.empty() || output_shards_directory_path.empty()) { + isc_throw(InvalidUsage, "neither the master database config file nor the output config" + "directory have been specified"); + } + + std::set filtered_shards_list; + std::string credentials_config; + isc::data::ConstElementPtr dhcp_config = + ConfigToolController::configureConfigToolConfigSource(dhcp_space, master_config_file); + SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + if (!ConfigToolController::configureConfigToolId(dhcp_config)) { + isc_throw(RunTimeFail, "config tool id not set in configuration as" + " mandatory 'instance-id' parameter is missing"); + } + + CfgDbAccessPtr cfg_db = srv_cfg->getCfgDbAccess(); + if (dhcp_space == DHCP_SPACE_V6) { + cfg_db->setAppendedParameters("universe=6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + cfg_db->setAppendedParameters("universe=4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + cfg_db->createSrvMasterCfgManagers(); + filtered_shards_list = shards_list; + } else if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::CONFIG_DATABASE) { + // Get 'config-database' component from the config. + auto config_database = dhcp_config->get("config-database"); + if (!config_database) { + isc_throw(isc::BadValue, "no mandatory 'config-database' entry in" + " the configuration"); + } + credentials_config = config_database->str(); + std::string database_name_key = "name"; + auto database_name_node = config_database->get(database_name_key); + if (!database_name_node) { + database_name_key = "keyspace"; + database_name_node = config_database->get(database_name_key); + } + if (!database_name_node) { + isc_throw(isc::BadValue, "neither 'name' nor 'keyspace' entry in 'config-database'"); + } + if (database_name_node->getType() != isc::data::Element::string) { + isc_throw(isc::BadValue, "'" + database_name_key + "' is not of type string"); + } + std::string shard_database = database_name_node->stringValue(); + std::set::const_iterator it = shards_list.find(shard_database); + if (it != shards_list.end()) { + std::cout << "Detected credentials for " << shard_database << std::endl; + filtered_shards_list.insert(database_name_node->stringValue()); + } + for (std::string const& shard_name : shards_list) { + std::set::const_iterator it = filtered_shards_list.find(shard_name); + if (it == filtered_shards_list.end()) { + std::cout << "Unknown credentials for shard " << shard_name << std::endl; + } + } + } else { + isc_throw(RunTimeFail, "unknown configuration type"); + } + + for (std::string const& shard_name : filtered_shards_list) { + std::string output_config_path = output_shards_directory_path; + + if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + std::string tool_id = CfgMgr::instance().getStagingCfg()->getInstanceId(); + + // Read the contents from database + SrvConfigMasterInfoPtr tool_config; + if (dhcp_space == DHCP_SPACE_V6) { + tool_config = + SrvConfigMasterMgrFactory::instance().getConfig6(tool_id + "@" + shard_name); + } else if (dhcp_space == DHCP_SPACE_V4) { + tool_config = + SrvConfigMasterMgrFactory::instance().getConfig4(tool_id + "@" + shard_name); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + + if (tool_config == SrvConfigMasterInfoPtr()) { + std::cout << "No credentials found in master database for config tool id: " + << tool_id << " for shard " << shard_name << std::endl; + continue; + } + credentials_config = tool_config->config_database_; + } + + if (output_config_path.length() > 0 && + output_config_path[output_config_path.length() - 1] != '/') { + output_config_path += '/'; + } + output_config_path += shard_name; + + if (!ConfigToolController::directoryExists(output_config_path)) { + if (!ConfigToolController::createDirectory(output_config_path)) { + isc_throw(RunTimeFail, "could not create the '" << output_config_path + << "' directory in order to store " + "the shard configuration"); + } + } + + getShardGenericConfig(dhcp_space, shard_name, credentials_config, output_config_path); + getShardJsonConfig(dhcp_space, shard_name, credentials_config, output_config_path); + } +} + +void ConfigToolController::getShardGenericConfig(DhcpSpaceType dhcp_space, + const std::string& shard_name, + const std::string& credentials_config, + const std::string& output_config_path) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_GET_GENERIC_CONFIG); + std::cout << "Starting to retrieve the GENERIC configuration for shard '" << shard_name + << "'..." << std::endl; + + if (shard_name.empty() || output_config_path.empty()) { + isc_throw(InvalidUsage, "neither the shard name nor the output config directory have" + "been specified"); + } + + SrvConfigInfoPtr configData = + ServerConfig::readShardGenericConfig(shard_name, dhcp_space, credentials_config); + + if (configData == SrvConfigInfoPtr()) { + isc_throw(RunTimeFail, "could not obtain configuration data from database."); + } + + std::string timestampFileFullPath = output_config_path + "/config.timestamp"; + std::ofstream timestamp_file(timestampFileFullPath.c_str()); + // Check for permissions to write into the file + if (!timestamp_file.is_open()) { + isc_throw(RunTimeFail, "cannot write the version file in the specified path."); + } + timestamp_file << configData->timestamp_; + timestamp_file.close(); + + boost::filesystem::path genericFileFullPath(output_config_path); + genericFileFullPath += "/config.generic"; + std::ofstream generic_file(genericFileFullPath.c_str()); + // Check for permissions to write into the file + if (!generic_file.is_open()) { + isc_throw(RunTimeFail, "cannot write the generic file in the specified path."); + } + generic_file << configData->generic_data_; + generic_file.close(); +} + +void ConfigToolController::getShardJsonConfig(DhcpSpaceType dhcp_space, + const std::string& shard_name, + const std::string& credentials_config, + const std::string& output_config_path) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_GET_JSON_CONFIG); + std::cout << "Starting to retrieve the JSON configuration for shard '" << shard_name << "'..." + << std::endl; + + if (shard_name.empty() || output_config_path.empty()) { + isc_throw(InvalidUsage, "neither the shard name nor the output config directory have " + "been specified"); + } + + SrvConfigInfoPtr configData = + ServerConfig::readShardJsonConfig(shard_name, dhcp_space, credentials_config); + + if (configData == SrvConfigInfoPtr()) { + isc_throw(RunTimeFail, "could not obtain configuration data from database."); + } + + std::string timestampFileFullPath = output_config_path + "/config.timestamp"; + std::ofstream timestamp_file(timestampFileFullPath.c_str()); + // Check for permissions to write into the file + if (!timestamp_file.is_open()) { + isc_throw(RunTimeFail, "cannot write the version file in the specified path."); + } + timestamp_file << configData->timestamp_; + timestamp_file.close(); + + boost::filesystem::path jsonFileFullPath(output_config_path); + jsonFileFullPath += "/config.json"; + std::ofstream json_file(jsonFileFullPath.c_str()); + // Check for permissions to write into the file + if (!json_file.is_open()) { + isc_throw(RunTimeFail, "cannot write the JSON file in the specified path."); + } + json_file << configData->json_data_; + json_file.close(); +} + +void ConfigToolController::updateShardConfig(DhcpSpaceType dhcp_space, + const std::string& shard_name, + const std::string& credentials_config, + const std::string& input_json_config_file, + const std::string& input_generic_config_file, + const std::string& input_timestamp_config_file) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_UPDATE_CONFIG).arg(shard_name); + std::cout << "Starting to update the configuration for shard '" << shard_name << "'..." + << std::endl; + + if (shard_name.empty()) { + isc_throw(InvalidUsage, "the shard name cannot be empty"); + } + + if (input_json_config_file.empty() || input_generic_config_file.empty() || + input_timestamp_config_file.empty()) { + isc_throw(InvalidUsage, "master input database credentials file, " + "the json config file, the generic config file and " + "the time timestamp file cannot be empty"); + } + + std::ifstream json_file(input_json_config_file.c_str(), std::ifstream::in); + // Check if the file has been opened + if (!json_file.is_open()) { + isc_throw(RunTimeFail, "cannot open json file " << input_json_config_file.c_str()); + } + std::string json_config((std::istreambuf_iterator(json_file)), + (std::istreambuf_iterator())); + json_file.close(); + + std::ifstream generic_file(input_generic_config_file.c_str(), std::ifstream::in); + // Check if the file has been opened + if (!generic_file.is_open()) { + isc_throw(RunTimeFail, "cannot open generic file" << input_generic_config_file.c_str()); + } + std::string generic_config((std::istreambuf_iterator(generic_file)), + (std::istreambuf_iterator())); + generic_file.close(); + + std::string config_timestamp; + std::ifstream timestamp_file(input_timestamp_config_file.c_str(), std::ifstream::in); + // Check if the file has been opened. + // The timestamp file may miss. + if (timestamp_file.is_open()) { + config_timestamp = std::string((std::istreambuf_iterator(timestamp_file)), + (std::istreambuf_iterator())); + timestamp_file.close(); + } + + IfaceMgr::instance().setServerMode(false); + + isc::data::ConstElementPtr answer, dhcp_element, json; + + json = isc::data::Element::fromJSON(json_config, true); + + if (dhcp_space == DHCP_SPACE_V6) { + dhcp_element = json->get("Dhcp6"); + if (dhcp_element) { + boost::scoped_ptr server(new Dhcpv6Srv(0)); + answer = configureDhcp6Server(*server, dhcp_element); + } + } else if (dhcp_space == DHCP_SPACE_V4) { + dhcp_element = json->get("Dhcp4"); + if (dhcp_element) { + boost::scoped_ptr server(new Dhcpv4Srv(0)); + answer = configureDhcp4Server(*server, dhcp_element); + } + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + + // Check answer. + if (dhcp_element) { + try { + int rcode = 0; + auto returnedAnswer = isc::config::parseAnswer(rcode, answer); + if (rcode != 0) { + isc_throw(RunTimeFail, "updateShardConfig() failed: " << *returnedAnswer); + } + } catch (const std::exception& ex) { + isc_throw(RunTimeFail, "updateShardConfig() failed: " << ex.what()); + } + } + + ServerConfig::updateShardConfig(shard_name, dhcp_space, config_timestamp, json_config, + generic_config, credentials_config); + + HostMgr::instance().syncReservations(); +} + +void ConfigToolController::masterSetServers(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& input_servers_directory_path, + const std::set& optional_shards_list) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_MASTER_UPDATE_SERVERS); + std::cout << "Starting to update the configuration in the master database..." << std::endl; + + if (dhcp_space == DHCP_SPACE_UNKNOWN) { + isc_throw(InvalidUsage, "No DHCP space provided"); + } + + if (master_config_file.empty() || input_servers_directory_path.empty()) { + isc_throw(InvalidUsage, "neither the master database config file " + "nor the input config directory cannot be empty"); + } + + isc::data::ConstElementPtr dhcp_config = + ConfigToolController::configureConfigToolConfigSource(dhcp_space, master_config_file); + SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + if (srv_cfg->getConfigurationType().type_ != CfgSrvConfigType::MASTER_DATABASE) { + isc_throw(isc::BadValue, "no mandatory 'master-database' entry in the configuration"); + } + + if (!ConfigToolController::configureConfigToolId(dhcp_config)) { + isc_throw(RunTimeFail, "config tool id not set in configuration as" + " mandatory 'instance-id' parameter is missing"); + } + + std::string tool_id = CfgMgr::instance().getStagingCfg()->getInstanceId(); + + CfgDbAccessPtr cfg_db = srv_cfg->getCfgDbAccess(); + if (dhcp_space == DHCP_SPACE_V6) { + cfg_db->setAppendedParameters("universe=6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + cfg_db->setAppendedParameters("universe=4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + cfg_db->createSrvMasterCfgManagers(); + + std::vector shard_directories; + populateDirectories(input_servers_directory_path, true, false, shard_directories); + + if (shard_directories.size() == 0) { + isc_throw(RunTimeFail, "there are no shards in the provided directory"); + } + + ServerConfig::ServerMasterConfigContainer serversMasterConfig; + + for (int idxShard = 0; idxShard < shard_directories.size(); idxShard++) { + std::string shard_name = shard_directories[idxShard].name; + std::string shard_cfg_path = shard_directories[idxShard].full_path; + + if (!optional_shards_list.empty()) { + std::set::const_iterator it = optional_shards_list.find(shard_name); + if (it == optional_shards_list.end()) { + continue; + } + } + + std::string credentials_cfg_file = shard_cfg_path + "/credentials.json"; + std::string credentials_config; + parseMasterCredentialsJson(shard_name, credentials_cfg_file, credentials_config); + std::string servers_config_file = shard_cfg_path + "/servers.json"; + parseMasterServersJson(shard_name, servers_config_file, serversMasterConfig); + ServerConfig::ServerMasterConfigPtr config_tool_config = + boost::make_shared(); + config_tool_config->config_database_ = credentials_config; + config_tool_config->config_database_name_ = shard_name; + config_tool_config->instance_id_ = tool_id + "@" + shard_name; + serversMasterConfig.push_back(config_tool_config); + } + + SrvConfigMasterMgrFactory::instance().startTransaction(); + try { + ServerConfig::updateSrvMasterConfig(dhcp_space, serversMasterConfig); + } catch (const std::exception& ex) { + SrvConfigMasterMgrFactory::instance().rollback(); + throw; + } + SrvConfigMasterMgrFactory::instance().commit(); +} + +void ConfigToolController::masterGetServers(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& output_shards_directory_path) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_MASTER_GET_SERVERS); + + std::cout << "Starting to retrieve the configuration from the master database..." << std::endl; + + if (dhcp_space == DHCP_SPACE_UNKNOWN) { + isc_throw(InvalidUsage, "No DHCP space provided"); + } + + if (master_config_file.empty() || output_shards_directory_path.empty()) { + isc_throw(InvalidUsage, "neither the master database config file " + "nor the output config directory cannot be empty"); + } + + isc::data::ConstElementPtr dhcp_config = + ConfigToolController::configureConfigToolConfigSource(dhcp_space, master_config_file); + SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + if (srv_cfg->getConfigurationType().type_ != CfgSrvConfigType::MASTER_DATABASE) { + isc_throw(isc::BadValue, "no mandatory 'master-database' entry in the configuration"); + } + + if (!ConfigToolController::configureConfigToolId(dhcp_config)) { + isc_throw(RunTimeFail, "config tool id not set in configuration as" + " mandatory 'instance-id' parameter is missing"); + } + + CfgDbAccessPtr cfg_db = srv_cfg->getCfgDbAccess(); + if (dhcp_space == DHCP_SPACE_V6) { + cfg_db->setAppendedParameters("universe=6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + cfg_db->setAppendedParameters("universe=4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + cfg_db->createSrvMasterCfgManagers(); + + SrvConfigMasterMgrFactory::instance().startTransaction(); + try { + std::set shards_list; + ServerConfig::getMasterSrvConfigShardsName(dhcp_space, shards_list); + + for (std::string const& shard_name : shards_list) { + masterGetShardServers(dhcp_space, shard_name, output_shards_directory_path); + } + } catch (const std::exception& ex) { + SrvConfigMasterMgrFactory::instance().rollback(); + throw; + } + SrvConfigMasterMgrFactory::instance().commit(); +} + +void ConfigToolController::masterGetShardServers(DhcpSpaceType dhcp_space, + const std::string& shard_name, + const std::string& output_shards_directory_path) { + std::cout << "Starting to retrieve the configuration for shard '" << shard_name + << "' from the master database..." << std::endl; + + std::vector server_info; + ServerConfig::getMasterSrvConfig(dhcp_space, shard_name, server_info); + + std::string json_data = masterSrvConfigDataToJSON(server_info); + std::string credentials_data = masterCredentialsToJSON(server_info); + + std::string output_config_path = output_shards_directory_path; + + if (output_config_path.length() > 0 && + output_config_path[output_config_path.length() - 1] != '/') { + output_config_path += '/'; + } + output_config_path += shard_name; + + if (!ConfigToolController::directoryExists(output_config_path)) { + if (!ConfigToolController::createDirectory(output_config_path)) { + isc_throw(RunTimeFail, "could not create the '" << output_config_path + << "' directory in order to store the " + "shard configuration"); + } + } + + boost::filesystem::path fileFullPath(output_config_path); + + boost::filesystem::path serversFileFullPath = fileFullPath; + serversFileFullPath += "/servers.json"; + + std::ofstream servers_file(serversFileFullPath.c_str()); + // Check for permissions to write into the file + if (!servers_file.is_open()) { + isc_throw(RunTimeFail, "cannot write the configuration file in the specified path."); + } + servers_file << json_data; + servers_file.close(); + + boost::filesystem::path credentialsFileFullPath = fileFullPath; + credentialsFileFullPath += "/credentials.json"; + + std::ofstream credentials_file(credentialsFileFullPath.c_str()); + // Check for permissions to write into the file + if (!credentials_file.is_open()) { + isc_throw(RunTimeFail, "cannot write the configuration file in the specified path."); + } + credentials_file << credentials_data; + credentials_file.close(); + + return; +} + +bool ConfigToolController::configureConfigToolId(isc::data::ConstElementPtr config_set) { + // Check if an 'instance-id' parameter is provided. + isc::data::ConstElementPtr tool_id = config_set->get("instance-id"); + if (tool_id) { + std::string id; + if (tool_id->getValue(id)) { + CfgMgr::instance().getStagingCfg()->getInstanceId() = id; + return true; + } + } + + return false; +} + +isc::data::ConstElementPtr +ConfigToolController::configureConfigToolConfigSource(DhcpSpaceType dhcp_space, + const std::string& file_name) { + isc::data::ConstElementPtr json; + isc::data::ConstElementPtr dhcp_node; + + try { + if (file_name.empty()) { + // Basic sanity check: file name must not be empty. + isc_throw(isc::BadValue, "the file name is empty"); + } + + // Read contents of the file and parse it as JSON + json = isc::data::Element::fromJSONFile(file_name, true); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, + "configuration file is expected to be a map, i.e., start with { and end with " + "} and contain at least an entry called 'Dhcp6/Dchp4' that itself is a map. " + << file_name + << " is a valid JSON, but its top element is not a map. Did you forget " + "to add { } around your configuration?"); + } + + // Get Dhcp6/Dhcp4 component from the config + + if (dhcp_space == DHCP_SPACE_V6) { + dhcp_node = json->get("Dhcp6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + dhcp_node = json->get("Dhcp4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + + if (!dhcp_node) { + isc_throw(isc::BadValue, "no mandatory 'Dhcp' entry in the configuration"); + } + + SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + + // check if a 'configuration-type' parameter is provided + isc::data::ConstElementPtr cfg_type_elem = dhcp_node->get("configuration-type"); + if (cfg_type_elem) { + std::string type; + if (cfg_type_elem->getValue(type)) { + // Valid values for "configuration-type" : "database" and "file" + if (type == "database") { + // The configuration type specifies that the server configuration should be + // read from the database. + + // Parse the 'master-database' parameter in order to update the config manager + // with the configuration database type and credentials. + isc::data::ConstElementPtr config_database = dhcp_node->get("master-database"); + if (config_database) { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::MASTER_DATABASE; + + DbAccessParser parser(DBType::MASTER_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_database); + } else { + // There has not been specified a "master-database" parameter. + // Check if a "config-database" parameter has been specified + isc::data::ConstElementPtr config_database = + dhcp_node->get("config-database"); + if (config_database) { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = + CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + parser.parse(cfg_db_access, config_database); + } + } + } else if (type == "file") { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::FILE; + isc_throw(isc::BadValue, "invalid value for 'configuration-type' parameter, " + "only 'database' type supported."); + } else { + // Update the config manager with the server configuration type + srv_cfg->getConfigurationType().type_ = CfgSrvConfigType::UNKNOWN; + isc_throw( + isc::BadValue, + "invalid value for 'configuration-type' parameter, can't process config."); + } + } + } else { + isc_throw(isc::BadValue, + "no mandatory 'configuration-type' entry in the configuration"); + } + } catch (const std::exception& ex) { + isc_throw(isc::BadValue, "configuration error using database credentials file '" + << file_name << "': " << ex.what()); + } + + return dhcp_node; +} + +void ConfigToolController::populateDirectories(const std::string& directory_path, + bool populateDirectories, + bool populateFiles, + std::vector& directoryContent) { + DIR* dir; + dirent* ent; + struct stat st; + + directoryContent.clear(); + + // Open directory. + dir = opendir(directory_path.c_str()); + if (dir == NULL) { + isc_throw(isc::BadValue, + "the specified directory does not exists '" << directory_path << "'"); + } + + while ((ent = readdir(dir)) != NULL) { + // Skip current directory '.'. + if (ent->d_name[0] == '.') { + continue; + } + + // Form full filename. + std::string const full_file_name = directory_path + '/' + ent->d_name; + + // For any error, continue. + if (stat(full_file_name.c_str(), &st) == -1) { + continue; + } + + // Check if it should be populated. + const bool is_directory = (st.st_mode & S_IFDIR) != 0; + if ((is_directory && populateDirectories) || (!is_directory && populateFiles)) { + FileInfo directory; + directory.name = ent->d_name; + directory.full_path = full_file_name; + directoryContent.push_back(directory); + } + } + + closedir(dir); +} + +// This function appends data to existing servers information in +// the 'serversMasterConfig' parameter. +// The 'serversMasterConfig' parameter is a vector of shared pointers +// in order to avoid to keep too much information on the stack. +void ConfigToolController::parseMasterServersJson( + const std::string& directory_shard_name, + const std::string& servers_config_file, + ServerConfig::ServerMasterConfigContainer& serversMasterConfig) { + try { + // Basic sanity check: file name must not be empty. + if (servers_config_file.empty()) { + isc_throw(isc::BadValue, "the file name is empty"); + } + + // Read contents of the file and parse it as JSON. + auto json = isc::data::Element::fromJSONFile(servers_config_file, true); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, + "configuration file is expected to be a map, i.e., start with { and end with " + "} and contain an entry called 'master-config' that itself is a map. " + << servers_config_file + << " is a valid JSON, but its top element is not a map. Did you forget " + "to add { } around your configuration?"); + } + + // Get 'config-database' component from the config. + auto config_database = json->get("config-database"); + if (!config_database) { + isc_throw(isc::BadValue, "no mandatory 'config-database' entry in the configuration"); + } + std::string database_name_key = "name"; + auto database_name_node = config_database->get(database_name_key); + if (!database_name_node) { + database_name_key = "keyspace"; + database_name_node = config_database->get(database_name_key); + } + if (!database_name_node) { + isc_throw(isc::BadValue, "neither 'name' nor 'keyspace' entry in 'config-database'"); + } + if (database_name_node->getType() != isc::data::Element::string) { + isc_throw(isc::BadValue, "'" + database_name_key + "' is not of type string"); + } + + if (database_name_node->stringValue() != directory_shard_name) { + isc_throw(isc::BadValue, "the shard's directory name '" + << directory_shard_name + << "' should be the same as the shard's database name " + << database_name_node->stringValue() + << "' specified in `config-database`."); + } + + // Get 'master-config' component from the config. + auto master_config_node = json->get("master-config"); + if (!master_config_node) { + isc_throw(isc::BadValue, "no mandatory 'master-config' entry in the configuration"); + } + + for (isc::data::ConstElementPtr server_config_node : master_config_node->listValue()) { + ServerConfig::ServerMasterConfigPtr server_config = + boost::make_shared(); + + server_config->config_database_ = config_database->str(); + server_config->config_database_name_ = directory_shard_name; + + for (ConfigPair param : server_config_node->mapValue()) { + std::string entry(param.first); + isc::data::ConstElementPtr value = param.second; + if (entry == "instance-id") { + if (value->getType() != isc::data::Element::string) { + isc_throw(isc::BadValue, "instance-id is not of type string"); + } + server_config->instance_id_ = value->stringValue(); + + } else if (entry == "server-config") { + server_config->server_config_ = value->str(); + } + } + + serversMasterConfig.push_back(server_config); + } + } catch (const std::exception& ex) { + isc_throw(isc::BadValue, "configuration error using server master config file '" + << servers_config_file << "': " << ex.what()); + } +} + +// This function receives credentials data for shard into credentialsConfig +void ConfigToolController::parseMasterCredentialsJson(const std::string& directory_shard_name, + const std::string& credentials_config_file, + std::string& credentials_config) { + try { + // Basic sanity check: file name must not be empty. + if (credentials_config_file.empty()) { + isc_throw(isc::BadValue, "the file name is empty"); + } + + // Read contents of the file and parse it as JSON. + auto json = isc::data::Element::fromJSONFile(credentials_config_file, true); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, + "configuration file is expected to be a map, i.e., start with { and end with " + "} and contain an entry called 'master-config' that itself is a map. " + << credentials_config_file + << " is a valid JSON, but its top element is not a map. Did you forget " + "to add { } around your configuration?"); + } + + // Get 'config-database' component from the config. + auto config_database = json->get("config-database"); + if (!config_database) { + isc_throw(isc::BadValue, "no mandatory 'config-database' entry in" + " the configuration"); + } + std::string database_name_key = "name"; + auto database_name_node = config_database->get(database_name_key); + if (!database_name_node) { + database_name_key = "keyspace"; + database_name_node = config_database->get(database_name_key); + } + if (!database_name_node) { + isc_throw(isc::BadValue, "neither 'name' nor 'keyspace' entry in 'config-database'"); + } + if (database_name_node->getType() != isc::data::Element::string) { + isc_throw(isc::BadValue, "'" + database_name_key + "' is not of type string"); + } + + if (database_name_node->stringValue() != directory_shard_name) { + isc_throw(isc::BadValue, "the shard's directory name '" + << directory_shard_name + << "' should be the same as the shard's database name " + << database_name_node->stringValue() + << "' specified in `config-database`."); + } + + credentials_config = config_database->str(); + } catch (const std::exception& ex) { + isc_throw(isc::BadValue, "configuration error using server master config file '" + << credentials_config_file << "': " << ex.what()); + } +} + +std::string +ConfigToolController::masterSrvConfigDataToJSON(std::vector& servers) { + // Tabs + std::string const tab = " "; + std::string const tabs[] = {"", tab, tab + tab, tab + tab + tab}; + + // Start serializing root-level JSON. + std::ostringstream json_data; + json_data << "{" << std::endl << tabs[1] << "\"master-config\": ["; + + // Serialize all 'master-config' elements. + bool first = true; + std::string config_database; + for (SrvConfigMasterInfoPtr const& server : servers) { + if (server->instance_id_ == CfgMgr::instance().getStagingCfg()->getInstanceId() + "@" + + server->config_database_name_) { + continue; + } + // Populate config-database while checking to be the same for all master servers. + if (config_database.empty()) { + // if 'config-database' was indeed empty than it should have been an empty map "{}". + if (server->config_database_.empty()) { + isc_throw(isc::BadValue, + "'config-database' is empty, would result in invalid JSON"); + } + config_database = server->config_database_; + } else if (config_database != server->config_database_) { + isc_throw(isc::BadValue, "expected the same 'config-database' for all master servers, " + "but they are different: " + << std::endl + << config_database << std::endl + << server->config_database_ << std::endl); + } + + // Add the comma between elements. + if (first) { + first = false; + } else { + json_data << ","; + } + + // Add the element's children. + json_data << std::endl + << tabs[2] << "{" << std::endl + << tabs[3] << "\"server-config\": " << server->server_config_ << "," << std::endl + << tabs[3] << "\"instance-id\": \"" << server->instance_id_ << "\"," << std::endl + << tabs[3] << "\"update_timestamp\": \"" << server->timestamp_ << "\"" + << std::endl + << tabs[2] << "}"; + } + + // End serializing root-level JSON. + json_data << std::endl // + << tabs[1] << "]," << std::endl + << tabs[1] << "\"config-database\": " << config_database << std::endl + << "}" << std::endl; + + return json_data.str(); +} + +std::string +ConfigToolController::masterCredentialsToJSON(std::vector& servers) { + // Tabs + std::string const tab = " "; + std::string const tabs[] = {"", tab, tab + tab, tab + tab + tab}; + + std::string config_database; + for (SrvConfigMasterInfoPtr const& server : servers) { + if (server->instance_id_ == CfgMgr::instance().getStagingCfg()->getInstanceId() + "@" + + server->config_database_name_) { + config_database = server->config_database_; + break; + } + } + + if (config_database.empty()) { + isc_throw(isc::BadValue, "'config-database' is empty, would result in invalid JSON"); + } + + // Serializing root-level JSON. + std::ostringstream json_data; + json_data << "{" << std::endl + << tabs[1] << "\"config-database\": " << config_database << std::endl + << "}" << std::endl; + + return json_data.str(); +} + +bool ConfigToolController::directoryExists(const std::string& path) { + DIR* pDir; + bool bExists = false; + + pDir = opendir(path.c_str()); + + if (pDir != NULL) { + bExists = true; + closedir(pDir); + } + + return bExists; +} + +bool ConfigToolController::createDirectory(const std::string& path) { + int status; + // read/write/search permissions for owner and group, + // and with read/search permissions for others + status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + return status == 0; +} + +} // namespace config_tool +} // namespace isc diff --git a/src/bin/kea_config_tool/config_tool_controller.h b/src/bin/kea_config_tool/config_tool_controller.h new file mode 100644 index 0000000000..5af7a1decc --- /dev/null +++ b/src/bin/kea_config_tool/config_tool_controller.h @@ -0,0 +1,191 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CONFIG_TOOL_H +#define CONFIG_TOOL_H + +#include + +#include +#include + +#include +#include +#include + +namespace isc { +namespace config_tool { + +/// @brief Exception thrown when the command line is invalid. +class InvalidUsage : public isc::Exception { +public: + InvalidUsage(const char* file, size_t line, const char* what, bool show_app_help = false) + : isc::Exception(file, line, what), show_app_help_(show_app_help) { + } + + bool getShowAppHelp() const { + return show_app_help_; + } + +private: + bool show_app_help_; +}; + +/// @brief Exceptions thrown when a method is unable to manipulate +/// (remove or rename) a file. +class RunTimeFail : public isc::Exception { +public: + RunTimeFail(const char* file, size_t line, const char* what) + : isc::Exception(file, line, what) { + } +}; + +class ConfigToolController { +public: + /// @brief Defines the application name, it may be used to locate + /// configuration data and appears in log statements. + static const char config_tool_app_name_[]; + + /// @brief Defines the executable name, by convention this should match + /// the executable name. + static const char config_tool_bin_name_[]; + + struct FileInfo { + std::string name; + std::string full_path; + }; + +public: + ConfigToolController(); + ~ConfigToolController(); + + /// ConfigToolController is a singleton class. This method returns reference + /// to its sole instance. + /// + /// \return the only existing instance of command options + static ConfigToolController& instance(); + + /// @brief Acts as the primary entry point to start execution + /// of the process. + /// + /// Provides the control logic to upload and download server configuration + /// file on database back-ends + /// + /// -# parse command line arguments + /// -# verify that it is the only instance + /// -# parse the database credentials file provided and connects to database + /// -# upload/download server configuration file + /// -# exit to the caller + /// + /// @param argc Number of strings in the @c argv array. + /// @param argv Array of arguments passed in via the program's main + /// function. + /// + /// @throw InvalidUsage if the command line parameters are invalid. + /// @throw BadValue if wrong values are provided in the database credentials + /// file + /// @throw RunTimeFail if an unexpected error occurs at runtime + /// (e.g. cannot connect to the database) + void launch(int argc, char* argv[]); + + ///@brief Start up the logging system + /// + /// @param test_mode indicates if we have have been started from the test + /// system (true) or are running normally (false) + void startLogger() const; + +protected: + /// @brief returns Kea version. + static std::string getVersion(bool extended); + + static void showApplicationVersion(bool show_detailed_version); + + static void setShardsConfig(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& input_shards_directory_path, + const std::set& optional_shards_list); + + static void getShardsConfig(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& output_shards_directory_path, + const std::set& shards_list); + + static void getShardGenericConfig(DhcpSpaceType dhcp_space, + const std::string& config_database_name, + const std::string& credentials_config, + const std::string& output_config_path); + + static void getShardJsonConfig(DhcpSpaceType dhcp_space, + const std::string& config_database_name, + const std::string& credentials_config, + const std::string& output_config_path); + + static void updateShardConfig(DhcpSpaceType dhcp_space, + const std::string& config_database_name, + const std::string& credentials_config, + const std::string& input_json_config_file, + const std::string& input_generic_config_file, + const std::string& input_timestamp_config_file); + + static void masterSetServers(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& input_servers_directory_path, + const std::set& optional_shards_list); + + static void masterGetServers(DhcpSpaceType dhcp_space, + const std::string& master_config_file, + const std::string& output_shards_directory_path); + + static void masterGetShardServers(DhcpSpaceType dhcp_space, + const std::string& config_database_name, + const std::string& output_shards_directory_path); + + static isc::data::ConstElementPtr configureConfigToolConfigSource(DhcpSpaceType dhcp_space, + const std::string& file_name); + + static bool configureConfigToolId(isc::data::ConstElementPtr config_set); + + /// @brief Utility methods + /// @{ + static void populateDirectories(const std::string& directory_path, + bool listFiles, + bool listDirectories, + std::vector& directoryContent); + + static void + parseMasterServersJson(const std::string& directory_shard_name, + const std::string& servers_config_file, + ServerConfig::ServerMasterConfigContainer& serversMasterConfig); + + static void parseMasterCredentialsJson(const std::string& directory_shard_name, + const std::string& credentials_config_file, + std::string& credentials_config); + + static std::string + masterSrvConfigDataToJSON(std::vector& server_info); + + static std::string + masterCredentialsToJSON(std::vector& server_info); + + static bool directoryExists(const std::string& path); + static bool createDirectory(const std::string& path); + /// @} +}; + +} // namespace config_tool +} // namespace isc + +#endif // CONFIG_TOOL_H diff --git a/src/bin/kea_config_tool/config_tool_log.cc b/src/bin/kea_config_tool/config_tool_log.cc new file mode 100644 index 0000000000..f7385c5f18 --- /dev/null +++ b/src/bin/kea_config_tool/config_tool_log.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +namespace isc { +namespace config_tool { + +/// @brief Defines the logger used within Kea Config Tool. +isc::log::Logger config_tool_logger(ConfigToolController::config_tool_app_name_); + +} // namespace config_tool +} // namespace isc diff --git a/src/bin/kea_config_tool/config_tool_log.h b/src/bin/kea_config_tool/config_tool_log.h new file mode 100644 index 0000000000..43aea424f9 --- /dev/null +++ b/src/bin/kea_config_tool/config_tool_log.h @@ -0,0 +1,33 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CONFIG_TOOOL_LOG_H +#define CONFIG_TOOOL_LOG_H + +#include +#include +#include + +namespace isc { +namespace config_tool { + +/// Define the logger for the "lfc" logging. +extern isc::log::Logger config_tool_logger; + +} // namespace config_tool +} // namespace isc + +#endif // LFC_LOG_H diff --git a/src/bin/kea_config_tool/config_tool_messages.mes b/src/bin/kea_config_tool/config_tool_messages.mes new file mode 100644 index 0000000000..53089f9882 --- /dev/null +++ b/src/bin/kea_config_tool/config_tool_messages.mes @@ -0,0 +1,62 @@ +# Copyright (C) 2016-2018 Deutsche Telekom AG. +# +# Author: Cristian Secareanu +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +$NAMESPACE isc::config_tool +% CONFIG_TOOL_FAIL_PROCESS : An error has occurred: %1, exiting with code %2 +This message is issued if KEA Config Tool has detected a failure when trying +to process the files. It includes a more specific error string. + +% CONFIG_TOOL_INVALID_USAGE : Invalid usage: %1, exiting with code %2 +This message is issued when the Kea Config Tool is used with improper +parameters. It includes a more specific error string. + + +% CONFIG_TOOL_GET_SHARDS_CONFIG Starting to retrieve the configuration of the provided shards +This message is issued as the the Kea config tool starts to retrieve the +JSON and GENERIC configuration from the provided shards databases. + +% CONFIG_TOOL_GET_GENERIC_CONFIG Starting to download the GENERIC configuration +This message is issued as the the Kea Config Tool starts to download the GENERIC +configuration. + +% CONFIG_TOOL_GET_JSON_CONFIG Starting to download the JSON configuration +This message is issued as the the Kea Config Tool starts to download the JSON +configuration. + +% CONFIG_TOOL_UPDATE_SHARDS_CONFIG Starting to update the configuration of the provided shards +This message is issued as the the Kea config tool starts to update the +JSON and GENERIC configuration into the provided shards databases. + +% CONFIG_TOOL_UPDATE_CONFIG Starting to update the shard '%1' configuration into database +This message is issued as the the Kea config tool starts to update the +JSON and GENERIC configuration into database. + +% CONFIG_TOOL_RUN_KEAADMIN Starting to run the kea-admin script +This message is issued as the the Kea config tool starts to execute the kea-admin +script in order to initialize de database backend. + +% CONFIG_TOOL_TERMINATE KEA Config Tool finished processing, exiting with code %1 +This message is issued when the KEA Config Tool process completes. It indicates +if the process was successful or not and that it has finished. + +% CONFIG_TOOL_MASTER_UPDATE_SERVERS Starting to update the servers configuration on the master database +This message is issued as the the Kea config tool starts to update the +master database configuration with the specified servers. + +% CONFIG_TOOL_MASTER_GET_SERVERS Starting to obtain the servers configuration from the master database +This message is issued as the the Kea config tool starts to retrieve the +master database servers configuration for all or the specified shards + diff --git a/src/bin/kea_config_tool/kea-config-tool.xml b/src/bin/kea_config_tool/kea-config-tool.xml new file mode 100644 index 0000000000..a35437f777 --- /dev/null +++ b/src/bin/kea_config_tool/kea-config-tool.xml @@ -0,0 +1,293 @@ +]> + +
+ kea-config-tool manual + + + + + Andrei + Pavel + + + Junior C++ Developer + SC QUALITANCE QBS SRL + + Created the manual for version 1.1.0 of + kea-config-tool. + + September 15, 2016 + kea-config-tool + + + + kea-config-tool + 8 + + Kea + 1.1.0 + + + + + kea-config-tool + DHCP configuration tool + + + + + 2016 + Deutsche Telekom AG. + + + + + + kea-config-tool + + + + + + + + + + + + + DESCRIPTION + kea-config-tool is a DHCP configuration + tool used by system administrators. It provides a way of + initializing and maintaining Kea server configuration when the + server uses a database backend for leases, reservations and + stateless configuration. The tool can be used to manage both DHCPv4 + and DHCPv6 server configurations. + Dependencies to the Kea server are statically linked so this + is a standalone tool and no Kea libraries are required in order to + run it. + + + TOP-LEVEL COMMANDS + + + + + A wrapper for kea-admin; it executes + kea-admin and returns it’s exit + status. + + + + + + + Insert or retrieve server configuration data into + or from shard databases. + + + + + + + Insert or retrieve server configuration data into + or from the master database. + + + + + + + Debug mode with exra verbose data + + + + + + + Display this help. + + + + + + + Print version number. + + + + + + + Print detailed version number. + + + + + + + OPTIONS + + + + + Used alongside the -shard + command. Inserts the provided server configuration into + the provided shard databases. + + + + + + + + Used alongside the -shard + command. Retrieves server configurations from the + provided shard databases and writes it to the provided + local directory. + + + + + + + + Used alongside the -master + command. Inserts the provided server configuration into + the master database. + + + + + + + + Used alongside the -master + command. Retrieves server configurations from the master + database and writes it to the provided local + directory. + + + + + + + + Specifies that this is a DHCPv4 operation. + + + + + + + + Specifies that this is a DHCPv6 operation. + + + + + + + PARAMETERS + + + master-config-file + + Configuration file which provides backend + credentials to the master database. + The following is a DHCPv4 configuration + example: + +{ + "Dhcp4": { + "configuration-type": "database", + "master-database": { + "type": "cql", + "keyspace": "kea_master", + "contact-points": "127.0.0.1", + "name": "keatest", + "user": "keatest", + "password": "keatest", + "port": "9042", + "max-statement-tries": 3 + } + } +} + + And a DHCPv6 configuration example: + +{ + "Dhcp6": { + "configuration-type": "database", + "master-database": { + "type": "cql", + "keyspace": "kea_master", + "contact-points": "127.0.0.1", + "name": "keatest", + "user": "keatest", + "password": "keatest", + "port": "9042", + "max-statement-tries": 3 + } + } +} + + + + + + + shards-directory-path + + When used with -set-* commands, + specifies the path from which the configuration is + loaded. + When used with -get-* commands, + specifies the path where the configuration is stored. + + + + + + list-of-shards + + Comma-separated shard names for which the command + is applied (for -set-* commands, + list-of-shards + is optional. If it is not provided, the command is + applied for all found shards. + + + + + + + SEE ALSO + + kea-admin8, + kea-dhcp48, + kea-dhcp68, + Kea Administrator Guide. + + + + +
diff --git a/src/bin/kea_config_tool/kea_admin.cc b/src/bin/kea_config_tool/kea_admin.cc new file mode 100644 index 0000000000..4eb0483051 --- /dev/null +++ b/src/bin/kea_config_tool/kea_admin.cc @@ -0,0 +1,104 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace isc::dhcp; + +static const uint32_t MAX_LINE = 256U; + +namespace isc { +namespace config_tool { + +KeaAdmin::KeaAdmin() { +} + +KeaAdmin::~KeaAdmin() { +} + +std::string KeaAdmin::getPathToExecutable() { + const ssize_t maxPathLength = FILENAME_MAX; + std::stringstream proc_path; + boost::scoped_array exe_path(new char[maxPathLength]); + + proc_path << "/proc/" << getpid() << "/exe"; + int bytes = readlink(proc_path.str().c_str(), exe_path.get(), maxPathLength); + if (bytes < 0) { + bytes = 0; + } + exe_path[bytes] = '\0'; + + return std::string(exe_path.get()); +} + +void KeaAdmin::runAdminScript(const std::string& admin_script_params) { + LOG_INFO(config_tool_logger, CONFIG_TOOL_RUN_KEAADMIN); + + std::string configToolPath = getPathToExecutable(); + std::string executable(dirname(const_cast(configToolPath.c_str()))); + executable += "/kea-admin"; + + // The command line is composed of the script's executable name, path and + // parameters. + std::string cmdLine = executable + " " + admin_script_params; + + FILE* fProcess; + int status; + + fProcess = popen(cmdLine.c_str(), "r"); + if (fProcess == NULL) { + isc_throw(RunTimeFail, "could not start run the admin script: " << cmdLine); + } + + char buffProcOutput[MAX_LINE]; + while (!feof(fProcess)) { + if (fgets(buffProcOutput, MAX_LINE, fProcess) == NULL) { + break; + } + std::cout << buffProcOutput; + } + std::cout << std::endl; + + status = pclose(fProcess); + + if (status == -1) { + isc_throw(RunTimeFail, + "could not close the admin script process. pclose error. " << cmdLine); + } else if (WIFEXITED(status)) { + int exitCode = WEXITSTATUS(status); + if (exitCode == 0) { + // The kea-admin process has returned success as error code. + return; + } + + isc_throw(RunTimeFail, "admin script error: " << exitCode); + + } else { + isc_throw(RunTimeFail, "admin script abnormal termination, error: " << status); + } +} + +} // namespace config_tool +} // namespace isc diff --git a/src/bin/kea_config_tool/kea_admin.h b/src/bin/kea_config_tool/kea_admin.h new file mode 100644 index 0000000000..a90a75e768 --- /dev/null +++ b/src/bin/kea_config_tool/kea_admin.h @@ -0,0 +1,48 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef KEA_ADMIN_H +#define KEA_ADMIN_H + +#include + +namespace isc { +namespace config_tool { + +/// @brief Used to run kea-admin tool from the Kea server. +class KeaAdmin { +private: + /// @brief Constructor + KeaAdmin(); + + /// @brief Destructor + ~KeaAdmin(); + + /// @brief Reads symbolic link from /proc//exe to get the absolute path + /// to the kea-admin executable. + static std::string getPathToExecutable(); + +public: + /// @brief Runs kea-admin with given parameters. + /// + /// @param admin_script_params parameters + static void runAdminScript(const std::string& admin_script_params); +}; + +} // namespace config_tool +} // namespace isc + +#endif // KEA_ADMIN_H diff --git a/src/bin/kea_config_tool/main.cc b/src/bin/kea_config_tool/main.cc new file mode 100644 index 0000000000..470139827c --- /dev/null +++ b/src/bin/kea_config_tool/main.cc @@ -0,0 +1,72 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include + +#include + +using namespace isc::config_tool; + +int main(int argc, char* argv[]) { + constexpr int EXIT_SUCCESSFUL = 0; + constexpr int EXIT_TRANSACTION_EXCEPTION = 1; + constexpr int EXIT_INVALID_USAGE = 2; + constexpr int EXIT_BOOST_EXCEPTION = 3; + constexpr int EXIT_STD_EXCEPTION = 4; + constexpr int EXIT_UNKNOWN_EXCEPTION = 5; + + int ret = EXIT_SUCCESSFUL; + + // Launch the controller passing in command line arguments. + // Exit program with the controller's return code. + try { + ConfigToolController config_controller; + config_controller.launch(argc, argv); + } catch (const isc::TransactionException& ex) { + ret = EXIT_TRANSACTION_EXCEPTION; + std::cerr << "TransactionException: " << ex.what() << ", exiting with code " << ret + << std::endl; + LOG_ERROR(config_tool_logger, CONFIG_TOOL_FAIL_PROCESS).arg(ex.what()).arg(ret); + } catch (const InvalidUsage& ex) { + ret = EXIT_INVALID_USAGE; + std::cerr << "Invalid usage: " << ex.what() << ", exiting with code " << ret << std::endl; + LOG_ERROR(config_tool_logger, CONFIG_TOOL_INVALID_USAGE).arg(ex.what()).arg(ret); + } catch (const boost::exception& ex) { + ret = EXIT_BOOST_EXCEPTION; + std::cerr << "boost::exception: " << boost::diagnostic_information(ex) + << ", exiting with code " << ret << std::endl; + LOG_ERROR(config_tool_logger, CONFIG_TOOL_FAIL_PROCESS) + .arg(boost::diagnostic_information(ex)) + .arg(ret); + } catch (const std::exception& ex) { + ret = EXIT_STD_EXCEPTION; + std::cerr << "std::exception: " << ex.what() << ", exiting with code " << ret << std::endl; + LOG_ERROR(config_tool_logger, CONFIG_TOOL_FAIL_PROCESS).arg(ex.what()).arg(ret); + } catch (...) { + ret = EXIT_UNKNOWN_EXCEPTION; + std::cerr << "unknown exception" + << ", exiting with code " << ret << std::endl; + LOG_ERROR(config_tool_logger, CONFIG_TOOL_FAIL_PROCESS).arg("unknown exception").arg(ret); + } + + LOG_INFO(config_tool_logger, CONFIG_TOOL_TERMINATE); + return ret; +} diff --git a/src/bin/kea_config_tool/server_config.cc b/src/bin/kea_config_tool/server_config.cc new file mode 100644 index 0000000000..9fed8b86fe --- /dev/null +++ b/src/bin/kea_config_tool/server_config.cc @@ -0,0 +1,278 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace isc::dhcp; + +namespace isc { +namespace config_tool { + +ServerConfig::ServerConfig() { +} + +ServerConfig::~ServerConfig() { +} + +SrvConfigInfoPtr ServerConfig::readShardGenericConfig(const std::string& config_database_name, + DhcpSpaceType dhcp_space, + const std::string& credentials_config) { + auto stagingCfg = CfgMgr::instance().getStagingCfg(); + CfgSrvConfigType& configurationType = stagingCfg->getConfigurationType(); + + isc::data::ConstElementPtr configDatabase = isc::data::Element::fromJSON(credentials_config); + if (!configDatabase) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Update the config manager with the server configuration type + configurationType.type_ = CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr stagingCfgDbAccess = stagingCfg->getCfgDbAccess(); + parser.parse(stagingCfgDbAccess, configDatabase); + + // Re-open configuration database with new parameters. + if (dhcp_space == DHCP_SPACE_V6) { + stagingCfgDbAccess->setAppendedParameters("universe=6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + stagingCfgDbAccess->setAppendedParameters("universe=4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + + try { + stagingCfgDbAccess->createSrvCfgManagers(); + } catch (const std::exception& ex) { + isc_throw(RunTimeFail, "unable to open the database: " << config_database_name + << ". Reason: " << ex.what()); + } + + SrvConfigInfoPtr configData; + + SrvConfigMgrFactory::instance().startTransaction(); + try { + // Read the contents from database. + if (dhcp_space == DHCP_SPACE_V6) { + configData = SrvConfigMgrFactory::instance().getGenericConfig6(); + } else if (dhcp_space == DHCP_SPACE_V4) { + configData = SrvConfigMgrFactory::instance().getGenericConfig4(); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + } catch (const std::exception& ex) { + SrvConfigMgrFactory::instance().rollback(); + throw; + } + SrvConfigMgrFactory::instance().commit(); + + if (configData == SrvConfigInfoPtr()) { + isc_throw(RunTimeFail, "no entry found in database"); + } + + return configData; +} + +SrvConfigInfoPtr ServerConfig::readShardJsonConfig(const std::string& config_database_name, + DhcpSpaceType dhcp_space, + const std::string& credentials_config) { + auto stagingCfg = CfgMgr::instance().getStagingCfg(); + CfgSrvConfigType& configurationType = stagingCfg->getConfigurationType(); + + isc::data::ConstElementPtr configDatabase = isc::data::Element::fromJSON(credentials_config); + if (!configDatabase) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Update the config manager with the server configuration type + configurationType.type_ = CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr stagingCfgDbAccess = stagingCfg->getCfgDbAccess(); + parser.parse(stagingCfgDbAccess, configDatabase); + + // Re-open configuration database with new parameters. + if (dhcp_space == DHCP_SPACE_V6) { + stagingCfgDbAccess->setAppendedParameters("universe=6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + stagingCfgDbAccess->setAppendedParameters("universe=4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + + try { + stagingCfgDbAccess->createSrvCfgManagers(); + } catch (const std::exception& ex) { + isc_throw(RunTimeFail, "unable to open the database: " << config_database_name + << ". Reason: " << ex.what()); + } + + SrvConfigInfoPtr configData; + + SrvConfigMgrFactory::instance().startTransaction(); + try { + // Read the contents from database + if (dhcp_space == DHCP_SPACE_V6) { + configData = SrvConfigMgrFactory::instance().getJsonConfig6(); + } else if (dhcp_space == DHCP_SPACE_V4) { + configData = SrvConfigMgrFactory::instance().getJsonConfig4(); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + } catch (const std::exception& ex) { + SrvConfigMgrFactory::instance().rollback(); + throw; + } + SrvConfigMgrFactory::instance().commit(); + + if (configData == SrvConfigInfoPtr()) { + isc_throw(RunTimeFail, "no entry found in database"); + } + + return configData; +} + +void ServerConfig::updateShardConfig(const std::string& config_database_name, + DhcpSpaceType dhcp_space, + const std::string& config_timestamp, + const std::string& json_config, + const std::string& generic_config, + const std::string& credentials_config) { + auto stagingCfg = CfgMgr::instance().getStagingCfg(); + CfgSrvConfigType& configurationType = stagingCfg->getConfigurationType(); + + isc::data::ConstElementPtr configDatabase = isc::data::Element::fromJSON(credentials_config); + if (!configDatabase) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Update the config manager with the server configuration type + configurationType.type_ = CfgSrvConfigType::CONFIG_DATABASE; + + DbAccessParser parser(DBType::CONFIG_DB); + CfgDbAccessPtr stagingCfgDbAccess = stagingCfg->getCfgDbAccess(); + parser.parse(stagingCfgDbAccess, configDatabase); + + // Re-open configuration database with new parameters. + if (dhcp_space == DHCP_SPACE_V6) { + stagingCfgDbAccess->setAppendedParameters("universe=6"); + } else if (dhcp_space == DHCP_SPACE_V4) { + stagingCfgDbAccess->setAppendedParameters("universe=4"); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + + try { + stagingCfgDbAccess->createSrvCfgManagers(); + } catch (const std::exception& ex) { + isc_throw(RunTimeFail, "unable to open the database: " << config_database_name + << ". Reason: " << ex.what()); + } + + // Write the contents to database + bool res = false; + int64_t timestamp = 0; + + stringstream timestampStream(config_timestamp); + timestampStream >> timestamp; + + SrvConfigMgrFactory::instance().startTransaction(); + try { + if (dhcp_space == DHCP_SPACE_V6) { + res = SrvConfigMgrFactory::instance().updateConfig6(timestamp, json_config, + generic_config); + } else if (dhcp_space == DHCP_SPACE_V4) { + res = SrvConfigMgrFactory::instance().updateConfig4(timestamp, json_config, + generic_config); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + } catch (const std::exception& ex) { + SrvConfigMgrFactory::instance().rollback(); + throw; + } + SrvConfigMgrFactory::instance().commit(); + + if (res == false) { + isc_throw(RunTimeFail, "could not update the database " + "server configuration because the timestamp of the database record " + "has been changed"); + } +} + +void ServerConfig::updateSrvMasterConfig(DhcpSpaceType dhcp_space, + ServerMasterConfigContainer& serversMasterConfig) { + if (dhcp_space == DHCP_SPACE_V6) { + SrvConfigMasterMgrFactory::instance().clearServersConfig6(); + } else if (dhcp_space == DHCP_SPACE_V4) { + SrvConfigMasterMgrFactory::instance().clearServersConfig4(); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + + for (ServerMasterConfigPtr server_config : serversMasterConfig) { + if (dhcp_space == DHCP_SPACE_V6) { + SrvConfigMasterMgrFactory::instance().addServerConfig6( + server_config->instance_id_, server_config->server_config_, + server_config->config_database_, server_config->config_database_name_); + } else if (dhcp_space == DHCP_SPACE_V4) { + SrvConfigMasterMgrFactory::instance().addServerConfig4( + server_config->instance_id_, server_config->server_config_, + server_config->config_database_, server_config->config_database_name_); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } + } +} + +void ServerConfig::getMasterSrvConfigShardsName(DhcpSpaceType dhcp_space, + std::set& shards_list) { + if (dhcp_space == DHCP_SPACE_V6) { + SrvConfigMasterMgrFactory::instance().getServersConfig6ShardsName(shards_list); + } else if (dhcp_space == DHCP_SPACE_V4) { + SrvConfigMasterMgrFactory::instance().getServersConfig4ShardsName(shards_list); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } +} + +void ServerConfig::getMasterSrvConfig(DhcpSpaceType dhcp_space, + const std::string& config_database_name, + std::vector& server_info) { + if (dhcp_space == DHCP_SPACE_V6) { + SrvConfigMasterMgrFactory::instance().getConfig6(config_database_name, server_info); + } else if (dhcp_space == DHCP_SPACE_V4) { + SrvConfigMasterMgrFactory::instance().getConfig4(config_database_name, server_info); + } else { + isc_throw(isc::BadValue, "invalid DHCP space type"); + } +} + +} // namespace config_tool +} // namespace isc diff --git a/src/bin/kea_config_tool/server_config.h b/src/bin/kea_config_tool/server_config.h new file mode 100644 index 0000000000..91fb3a5b83 --- /dev/null +++ b/src/bin/kea_config_tool/server_config.h @@ -0,0 +1,80 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DHCP_SERVER_CONFIG_H +#define DHCP_SERVER_CONFIG_H + +#include +#include +#include + +#include +#include + +namespace isc { +namespace config_tool { + +/* DHCP space types: */ +enum DhcpSpaceType { DHCP_SPACE_UNKNOWN, DHCP_SPACE_V4, DHCP_SPACE_V6 }; + +class ServerConfig { +public: + struct ServerMasterConfig { + std::string instance_id_; + std::string server_config_; + std::string config_database_; + std::string config_database_name_; + }; + + typedef boost::shared_ptr ServerMasterConfigPtr; + typedef std::vector ServerMasterConfigContainer; + +private: + ServerConfig(); + ~ServerConfig(); + +public: + static isc::dhcp::SrvConfigInfoPtr + readShardGenericConfig(const std::string& config_database_name, + DhcpSpaceType dhcp_space, + const std::string& credentials_config); + + static isc::dhcp::SrvConfigInfoPtr readShardJsonConfig(const std::string& config_database_name, + DhcpSpaceType dhcp_space, + const std::string& credentials_config); + + static void updateShardConfig(const std::string& config_database_name, + DhcpSpaceType dhcp_space, + const std::string& config_timestamp, + const std::string& json_config, + const std::string& generic_config, + const std::string& credentials_config); + + static void updateSrvMasterConfig(DhcpSpaceType dhcp_space, + ServerMasterConfigContainer& serversMasterConfig); + + static void getMasterSrvConfigShardsName(DhcpSpaceType dhcp_space, + std::set& shards_list); + + static void getMasterSrvConfig(DhcpSpaceType dhcp_space, + const std::string& config_database_name, + std::vector& server_info); +}; + +} // namespace config_tool +} // namespace isc + +#endif // DHCP_SERVER_CONFIG_H diff --git a/src/bin/kea_config_tool/tests/.gitignore b/src/bin/kea_config_tool/tests/.gitignore new file mode 100644 index 0000000000..d2e2f56bdb --- /dev/null +++ b/src/bin/kea_config_tool/tests/.gitignore @@ -0,0 +1,2 @@ +/run_unittests + diff --git a/src/bin/kea_config_tool/tests/Makefile.am b/src/bin/kea_config_tool/tests/Makefile.am new file mode 100644 index 0000000000..dddf476594 --- /dev/null +++ b/src/bin/kea_config_tool/tests/Makefile.am @@ -0,0 +1,38 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin +AM_CPPFLAGS += -I$(srcdir)/.. -I$(builddir)/.. +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += run_unittests +run_unittests_SOURCES = run_unittests.cc +run_unittests_SOURCES += command_options_unittest.cc +run_unittests_SOURCES += config_tool_controller_unittest.cc +run_unittests_SOURCES += kea_admin_unittest.cc +run_unittests_SOURCES += server_config_unittest.cc +run_unittests_SOURCES += command_options_helper.h + +run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) + +run_unittests_LDADD = $(top_builddir)/src/bin/kea_config_tool/libkeaconfigtool.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +run_unittests_LDADD += $(CRYPTO_LIBS) $(BOOST_LIBS) $(GTEST_LDADD) +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/bin/kea_config_tool/tests/command_options_helper.h b/src/bin/kea_config_tool/tests/command_options_helper.h new file mode 100644 index 0000000000..6675f5f102 --- /dev/null +++ b/src/bin/kea_config_tool/tests/command_options_helper.h @@ -0,0 +1,147 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMAND_OPTIONS_HELPER_H +#define COMMAND_OPTIONS_HELPER_H + +#include "../command_options.h" + +#include + +#include +#include +#include +#include +#include + +namespace isc { +namespace config_tool { + +/// \brief Command Options Helper class. +/// +/// This helper class can be shared between unit tests that +/// need to initialize CommandOptions objects and feed it with +/// specific command line. The command line can be given as a +/// string representing program name, options and arguments. +/// The static method exposed by this class can be used to +/// tokenize this string into array of C-strings that are later +/// consumed by \ref CommandOptions::parse. The state of the +/// CommandOptions object is reset every time the process +/// function is invoked. Also, when command line parsing is +/// ended the array of C-string is freed from the memory. +class CommandOptionsHelper { +public: + /// \brief Wrapper class for allocated argv[] array. + /// + /// This class wraps allocated char** array and ensures that memory + /// allocated for this array is freed at the end o the scope. + class ArgvPtr { + public: + /// \brief Constructor. + /// + /// \param argv array of C-strings. + /// \param number of C-strings in the array. + ArgvPtr(char** argv, int argc) : argv_(argv), argc_(argc) { + } + + /// \brief Destructor. + /// + /// Deallocates wrapped array of C-strings. + ~ArgvPtr() { + if (argv_ != NULL) { + for (int i = 0; i < argc_; ++i) { + delete[](argv_[i]); + argv_[i] = NULL; + } + delete[](argv_); + } + } + + /// \brief Return the array of C-strings. + /// + /// \return array of C-strings. + char** getArgv() const { + return (argv_); + } + + /// \brief Return C-strings counter. + /// + /// \return C-strings counter. + int getArgc() const { + return (argc_); + } + + public: + char** argv_; ///< array of C-strings being wrapped. + int argc_; ///< number of C-strings. + }; + + /// \brief Parse command line provided as string. + /// + /// Method transforms the string representing command line + /// to the array of C-strings consumed by the + /// \ref CommandOptions::parse function and performs + /// parsing. + /// + /// \param cmdline command line provided as single string. + /// \return true if program has been run in help or version mode ('h' or 'v' + /// flag). + static void process(const std::string& cmdline) { + CommandOptions& opt = CommandOptions::instance(); + int argc = 0; + char** argv = tokenizeString(cmdline, argc); + ArgvPtr args(argv, argc); + opt.resetInternalData(); + opt.parse(args.getArgc(), args.getArgv()); + } + +private: + /// \brief Split string to the array of C-strings. + /// + /// \param text_to_split string to be splited. + /// \param [out] num number of substrings returned. + /// \param std::bad_alloc if allocation of C-strings failed. + /// \return array of C-strings created from split. + static char** tokenizeString(const std::string& text_to_split, int& num) { + char** results = NULL; + // Tokenization with std streams + std::stringstream text_stream(text_to_split); + // Iterators to be used for tokenization + std::istream_iterator text_iterator(text_stream); + std::istream_iterator text_end; + // Tokenize string (space is a separator) using begin and end iterators + std::vector tokens(text_iterator, text_end); + + if (!tokens.empty()) { + // Allocate array of C-strings where we will store tokens + results = new char*[tokens.size()]; + // Store tokens in C-strings array + for (size_t i = 0; i < tokens.size(); ++i) { + size_t cs_size = tokens[i].length() + 1; + char* cs = new char[cs_size]; + strncpy(cs, tokens[i].c_str(), cs_size); + assert(cs[cs_size - 1] == '\0'); + results[i] = cs; + } + // Return number of tokens to calling function + num = tokens.size(); + } + return results; + } +}; + +} // namespace config_tool +} // namespace isc + +#endif // COMMAND_OPTIONS_HELPER_H diff --git a/src/bin/kea_config_tool/tests/command_options_unittest.cc b/src/bin/kea_config_tool/tests/command_options_unittest.cc new file mode 100644 index 0000000000..4bd8c3b484 --- /dev/null +++ b/src/bin/kea_config_tool/tests/command_options_unittest.cc @@ -0,0 +1,681 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +#include "command_options_helper.h" + +using namespace isc::config_tool; +/* +/// \brief Test Fixture Class +/// +/// This test fixture class is used to perform +/// unit tests on kea-config-tool CommandOptions class. +class CommandOptionsTest : public virtual ::testing::Test +{ +public: + /// \brief Default Constructor + CommandOptionsTest() { } + +protected: + /// \brief Parse command line and cleanup + /// + /// The method tokenizes command line to array of C-strings, + /// parses arguments using CommandOptions class to set + /// its data members and de-allocates array of C-strings. + /// + /// \param cmdline Command line to parse. + /// \throws std::bad allocation if tokenization failed. + /// \return true if program has been run in help or version mode ('h' or 'v' flag). + void process(const std::string& cmdline) { + CommandOptionsHelper::process(cmdline); + } + + /// \brief Check default initialized values + /// + /// Check if initialized values are correct + void checkDefaults() { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool --help")); + + // default mac + const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; + + // Check if DUID is initialized. The DUID-LLT is expected + // to start with DUID_LLT value of 1 and hardware ethernet + // type equal to 1 (HWETHER_TYPE). + const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 }; + // We assume DUID-LLT length 14. This includes 4 octets of + // DUID_LLT value, two octets of hardware type, 4 octets + // of time value and 6 octets of variable link layer (MAC) + // address. + const int duid_llt_size = 14; + // DUID is not given from the command line but it is supposed + // to be initialized by the CommandOptions private method + // generateDuidTemplate(). + std::vector v2 = opt.getDuidTemplate(); + ASSERT_EQ(duid_llt_size, opt.getDuidTemplate().size()); + EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4, + duid_llt_and_hw)); + // Check time field contents. + ptime now = microsec_clock::universal_time(); + ptime duid_epoch(from_iso_string("20000101T000000")); + time_period period(duid_epoch, now); + uint32_t duration_sec = period.length().total_seconds(); + // Read time from the template generated. + uint32_t duration_from_template = 0; + memcpy(&duration_from_template, &v2[4], 4); + duration_from_template = htonl(duration_from_template); + // In special cases, we may have overflow in time field + // so we give ourselves the margin of 10 seconds here. + // If time value has been set more then 10 seconds back + // it is safe to compare it with the time value generated + // from now. + if (duration_from_template > 10) { + EXPECT_GE(duration_sec, duration_from_template); + } + + EXPECT_EQ(0, opt.getBase().size()); + EXPECT_EQ(0, opt.getNumRequests().size()); + EXPECT_EQ(0, opt.getPeriod()); + for (size_t i = 0; i < opt.getDropTime().size(); ++i) { + EXPECT_DOUBLE_EQ(1, opt.getDropTime()[i]); + } + ASSERT_EQ(opt.getMaxDrop().size(), opt.getMaxDropPercentage().size()); + for (size_t i = 0; i < opt.getMaxDrop().size(); ++i) { + EXPECT_EQ(0, opt.getMaxDrop()[i]); + EXPECT_EQ(0, opt.getMaxDropPercentage()[i]); + } + EXPECT_EQ("", opt.getLocalName()); + EXPECT_FALSE(opt.isInterface()); + EXPECT_EQ(0, opt.getPreload()); + EXPECT_EQ(1, opt.getAggressivity()); + EXPECT_EQ(0, opt.getLocalPort()); + EXPECT_FALSE(opt.isSeeded()); + EXPECT_EQ(0, opt.getSeed()); + EXPECT_FALSE(opt.isBroadcast()); + EXPECT_FALSE(opt.isRapidCommit()); + EXPECT_FALSE(opt.isUseFirst()); + EXPECT_EQ(0, opt.getTemplateFiles().size()); + EXPECT_EQ(0, opt.getTransactionIdOffset().size()); + EXPECT_EQ(0, opt.getRandomOffset().size()); + EXPECT_GT(0, opt.getElapsedTimeOffset()); + EXPECT_GT(0, opt.getServerIdOffset()); + EXPECT_GT(0, opt.getRequestedIpOffset()); + EXPECT_EQ("", opt.getDiags()); + EXPECT_EQ("", opt.getWrapped()); + EXPECT_EQ("192.168.0.1", opt.getServerName()); + } +}; + +TEST_F(CommandOptionsTest, Defaults) { + EXPECT_NO_THROW(process("kea-config-tool all")); + checkDefaults(); +} + +TEST_F(CommandOptionsTest, HelpVersion) { + // The parser is supposed to return true if 'h' or 'v' options + // are specified. + EXPECT_NO_THROW(process("kea-config-tool -h")); + EXPECT_NO_THROW(process("kea-config-tool -v")); + EXPECT_NO_THROW(process("kea-config-tool -h -v")); + EXPECT_NO_THROW(process("kea-config-tool -6 -l ethx -h all")); + EXPECT_NO_THROW(process("kea-config-tool -l ethx -v all")); + // No 'h' or 'v' option specified. The false value + // should be returned. + EXPECT_FALSE(process("kea-config-tool -l ethx all")); +} + +TEST_F(CommandOptionsTest, UseFirst) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -1 -B -l ethx all")); + EXPECT_TRUE(opt.isUseFirst()); +} +TEST_F(CommandOptionsTest, IpVersion) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -6 -l ethx -c -i all")); + EXPECT_EQ(6, opt.getIpVersion()); + EXPECT_EQ("ethx", opt.getLocalName()); + EXPECT_TRUE(opt.isRapidCommit()); + EXPECT_FALSE(opt.isBroadcast()); + process("kea-config-tool -4 -B -l ethx all"); + EXPECT_EQ(4, opt.getIpVersion()); + EXPECT_TRUE(opt.isBroadcast()); + EXPECT_FALSE(opt.isRapidCommit()); + + // Negative test cases + // -4 and -6 must not coexist + EXPECT_THROW(process("kea-config-tool -4 -6 -l ethx all"), isc::InvalidParameter); + // -6 and -B must not coexist + EXPECT_THROW(process("kea-config-tool -6 -B -l ethx all"), isc::InvalidParameter); + // -c and -4 (default) must not coexist + EXPECT_THROW(process("kea-config-tool -c -l ethx all"), isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Rate) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -4 -r 10 -l ethx all")); + EXPECT_EQ(10, opt.getRate()); + + // Negative test cases + // Rate must not be 0 + EXPECT_THROW(process("kea-config-tool -4 -r 0 -l ethx all"), + isc::InvalidParameter); + // -r must be specified to use -n, -p and -D + EXPECT_THROW(process("kea-config-tool -6 -t 5 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -4 -n 150 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -6 -p 120 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -4 -D 1400 -l ethx all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, RenewRate) { + CommandOptions& opt = CommandOptions::instance(); + // If -f is specified together with -r the command line should + // be accepted and the renew rate should be set. + EXPECT_NO_THROW(process("kea-config-tool -6 -r 10 -f 10 -l ethx all")); + EXPECT_EQ(10, opt.getRenewRate()); + // Check that the release rate can be set to different value than + // rate specified as -r. Also, swap -f and -r to make sure + // that order doesn't matter. + EXPECT_NO_THROW(process("kea-config-tool -6 -f 5 -r 10 -l ethx all")); + EXPECT_EQ(5, opt.getRenewRate()); + // Renew rate should also be accepted for DHCPv4 case. + EXPECT_NO_THROW(process("kea-config-tool -4 -f 5 -r 10 -l ethx all")); + EXPECT_EQ(5, opt.getRenewRate()); + // The renew rate should not be greater than the rate. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -f 11 -l ethx all"), + isc::InvalidParameter); + // The renew-rate of 0 is invalid. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -f 0 -l ethx all"), + isc::InvalidParameter); + // The negative renew-rate is invalid. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -f -5 -l ethx all"), + isc::InvalidParameter); + // If -r is not specified the -f should not + // be accepted. + EXPECT_THROW(process("kea-config-tool -6 -f 10 -l ethx all"), + isc::InvalidParameter); + // Renew rate should be specified. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -f -l ethx all"), + isc::InvalidParameter); + + // -f and -i are mutually exclusive + EXPECT_THROW(process("kea-config-tool -6 -r 10 -f 10 -l ethx -i all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, ReleaseRate) { + CommandOptions& opt = CommandOptions::instance(); + // If -F is specified together with -r the command line should + // be accepted and the release rate should be set. + EXPECT_NO_THROW(process("kea-config-tool -6 -r 10 -F 10 -l ethx all")); + EXPECT_EQ(10, opt.getReleaseRate()); + // Check that the release rate can be set to different value than + // rate specified as -r. Also, swap -F and -r to make sure + // that order doesn't matter. + EXPECT_NO_THROW(process("kea-config-tool -6 -F 5 -r 10 -l ethx all")); + EXPECT_EQ(5, opt.getReleaseRate()); + // The release rate should not be greater than the rate. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -F 11 -l ethx all"), + isc::InvalidParameter); + // The release-rate of 0 is invalid. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -F 0 -l ethx all"), + isc::InvalidParameter); + // The negative release-rate is invalid. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -F -5 -l ethx all"), + isc::InvalidParameter); + // If -r is not specified the -F should not + // be accepted. + EXPECT_THROW(process("kea-config-tool -6 -F 10 -l ethx all"), + isc::InvalidParameter); + // Currently the -F can be specified for IPv6 mode + // only. + EXPECT_THROW(process("kea-config-tool -4 -r 10 -F 10 -l ethx all"), + isc::InvalidParameter); + // Release rate should be specified. + EXPECT_THROW(process("kea-config-tool -6 -r 10 -F -l ethx all"), + isc::InvalidParameter); + + // -F and -i are mutually exclusive + EXPECT_THROW(process("kea-config-tool -6 -r 10 -F 10 -l ethx -i all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, ReleaseRenew) { + CommandOptions& opt = CommandOptions::instance(); + // It should be possible to specify the -F, -f and -r options. + EXPECT_NO_THROW(process("kea-config-tool -6 -r 10 -F 3 -f 5 -l ethx all")); + EXPECT_EQ(10, opt.getRate()); + EXPECT_EQ(3, opt.getReleaseRate()); + EXPECT_EQ(5, opt.getRenewRate()); + // It should be possible to specify the -F and -f with the values which + // sum is equal to the rate specified as -r. + EXPECT_NO_THROW(process("kea-config-tool -6 -r 8 -F 3 -f 5 -l ethx all")); + EXPECT_EQ(8, opt.getRate()); + EXPECT_EQ(3, opt.getReleaseRate()); + EXPECT_EQ(5, opt.getRenewRate()); + // Check that the sum of the release and renew rate is not greater + // than the rate specified as -r. + EXPECT_THROW(process("kea-config-tool -6 -F 6 -f 5 -r 10 -l ethx all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, ReportDelay) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -r 100 -t 17 -l ethx all")); + EXPECT_EQ(17, opt.getReportDelay()); + + // Negative test cases + // -t must be positive integer + EXPECT_THROW(process("kea-config-tool -r 10 -t -8 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -r 10 -t 0 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -r 10 -t s -l ethx all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, ClientsNum) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -R 200 -l ethx all")); + EXPECT_EQ(200, opt.getClientsNum()); + process("kea-config-tool -R 0 -l ethx all"); + EXPECT_EQ(0, opt.getClientsNum()); + + // Negative test cases + // Number of clients must be non-negative integer + EXPECT_THROW(process("kea-config-tool -R -5 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -R gs -l ethx all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Base) { + CommandOptions& opt = CommandOptions::instance(); + uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 }; + uint8_t duid[14] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x10, 0x11, 0x1F, 0x14 }; + // Test DUID and MAC together. + EXPECT_NO_THROW(process("kea-config-tool -b DUID=0101010101010101010110111F14" + " -b MAC=10::20::30::40::50::60" + " -l 127.0.0.1 all")); + std::vector v1 = opt.getMacTemplate(); + std::vector v2 = opt.getDuidTemplate(); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + // Test valid DUID. + EXPECT_NO_THROW( + process("kea-config-tool -b duid=0101010101010101010110111F14 -l 127.0.0.1 all") + ); + + ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size()); + EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + // Test mix of upper/lower case letters. + EXPECT_NO_THROW(process("kea-config-tool -b DuiD=0101010101010101010110111F14" + " -b Mac=10::20::30::40::50::60" + " -l 127.0.0.1 all")); + v1 = opt.getMacTemplate(); + v2 = opt.getDuidTemplate(); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + // Use "ether" instead of "mac". + EXPECT_NO_THROW(process("kea-config-tool -b ether=10::20::30::40::50::60" + " -l 127.0.0.1 all")); + v1 = opt.getMacTemplate(); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + // Use "ETHER" in upper case. + EXPECT_NO_THROW(process("kea-config-tool -b ETHER=10::20::30::40::50::60" + " -l 127.0.0.1 all")); + v1 = opt.getMacTemplate(); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + // "t" is invalid character in DUID + EXPECT_THROW(process("kea-config-tool -6 -l ethx -b " + "duid=010101010101010101t110111F14 all"), + isc::InvalidParameter); + // "3x" is invalid value in MAC address + EXPECT_THROW(process("kea-config-tool -b mac=10::2::3x::4::5::6 -l ethx all"), + isc::InvalidParameter); + // Base is not specified + EXPECT_THROW(process("kea-config-tool -b -l ethx all"), + isc::InvalidParameter); + // Typo: should be mac= instead of mc= + EXPECT_THROW(process("kea-config-tool -l ethx -b mc=00:01:02:03::04:05 all"), + isc::InvalidParameter); + // Too short DUID (< 6). + EXPECT_THROW(process("kea-config-tool -l ethx -b duid=00010203 all"), + isc::InvalidParameter); + // Odd number of digits. + EXPECT_THROW(process("kea-config-tool -l ethx -b duid=000102030405060 all"), + isc::InvalidParameter); + // Too short MAC (!= 6). + EXPECT_THROW(process("kea-config-tool -l ethx -b mac=00:01:02:04 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, DropTime) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -l ethx -d 12 all")); + ASSERT_EQ(2, opt.getDropTime().size()); + EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]); + EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]); + + EXPECT_NO_THROW(process("kea-config-tool -l ethx -d 2 -d 4.7 all")); + ASSERT_EQ(2, opt.getDropTime().size()); + EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]); + EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]); + + // Negative test cases + // Drop time must not be negative + EXPECT_THROW(process("kea-config-tool -l ethx -d -2 -d 4.7 all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -l ethx -d -9.1 -d 0 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, TimeOffset) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -l ethx -T file1.x -T file2.x -E 4 all")); + EXPECT_EQ(4, opt.getElapsedTimeOffset()); + + // Negative test cases + // Argument -E must be used with -T + EXPECT_THROW(process("kea-config-tool -l ethx -E 3 -i all"), + isc::InvalidParameter); + // Value in -E not specified + EXPECT_THROW(process("kea-config-tool -l ethx -T file.x -E -i all"), + isc::InvalidParameter); + // Value for -E must not be negative + EXPECT_THROW(process("kea-config-tool -l ethx -E -3 -T file.x all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, ExchangeMode) { + CommandOptions& opt = CommandOptions::instance(); + process("kea-config-tool -l ethx -i all"); + EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode()); + + // Negative test cases + // No template file specified + EXPECT_THROW(process("kea-config-tool -i -l ethx -X 3 all"), + isc::InvalidParameter); + // Offsets can't be used in simple exchanges (-i) + EXPECT_THROW(process("kea-config-tool -i -l ethx -O 2 -T file.x all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -i -l ethx -E 3 -T file.x all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -i -l ethx -S 1 -T file.x all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -i -l ethx -I 2 -T file.x all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Offsets) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx " + "-X3 -T file1.x -T file2.x all")); + EXPECT_EQ(2, opt.getRequestedIpOffset()); + EXPECT_EQ(5, opt.getElapsedTimeOffset()); + EXPECT_EQ(3, opt.getServerIdOffset()); + ASSERT_EQ(2, opt.getRandomOffset().size()); + EXPECT_EQ(30, opt.getRandomOffset()[0]); + EXPECT_EQ(30, opt.getRandomOffset()[1]); + ASSERT_EQ(2, opt.getTransactionIdOffset().size()); + EXPECT_EQ(7, opt.getTransactionIdOffset()[0]); + EXPECT_EQ(3, opt.getTransactionIdOffset()[1]); + + // Negative test cases + // IP offset/IA_NA offset must be positive + EXPECT_THROW(process("kea-config-tool -6 -I 0 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -6 -I -4 -l ethx all"), + isc::InvalidParameter); + + // TODO - other negative cases +} + +TEST_F(CommandOptionsTest, LocalPort) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -l ethx -L 2000 all")); + EXPECT_EQ(2000, opt.getLocalPort()); + + // Negative test cases + // Local port must be between 0..65535 + EXPECT_THROW(process("kea-config-tool -l ethx -L -2 all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -l ethx -L all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -l ethx -L 65540 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Preload) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -1 -P 3 -l ethx all")); + EXPECT_EQ(3, opt.getPreload()); + + // Negative test cases + // Number of preload packages must not be negative integer + EXPECT_THROW(process("kea-config-tool -P -1 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -P -3 -l ethx all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Seed) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -6 -P 2 -s 23 -l ethx all")); + EXPECT_EQ(23, opt.getSeed()); + EXPECT_TRUE(opt.isSeeded()); + + EXPECT_NO_THROW(process("kea-config-tool -6 -P 2 -s 0 -l ethx all")); + EXPECT_EQ(0, opt.getSeed()); + EXPECT_FALSE(opt.isSeeded()); + + // Negative test cases + // Seed must be non-negative integer + EXPECT_THROW(process("kea-config-tool -6 -P 2 -s -5 -l ethx all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -6 -P 2 -s -l ethx all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, TemplateFiles) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -T file1.x -l ethx all")); + ASSERT_EQ(1, opt.getTemplateFiles().size()); + EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]); + + EXPECT_NO_THROW(process("kea-config-tool -T file1.x -s 12 -w start -T file2.x -4 -l ethx all")); + ASSERT_EQ(2, opt.getTemplateFiles().size()); + EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]); + EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]); + + // Negative test cases + // No template file specified + EXPECT_THROW(process("kea-config-tool -s 12 -T -l ethx all"), + isc::InvalidParameter); + // Too many template files specified + EXPECT_THROW(process("kea-config-tool -s 12 -l ethx -T file.x " + "-T file.x -T file.x all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Wrapped) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -B -w start -i -l ethx all")); + EXPECT_EQ("start", opt.getWrapped()); + + // Negative test cases + // Missing command after -w, expected start/stop + EXPECT_THROW(process("kea-config-tool -B -i -l ethx -w all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Diagnostics) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -l ethx -i -x asTe all")); + EXPECT_EQ("asTe", opt.getDiags()); + + // Negative test cases + // No diagnostics string specified + EXPECT_THROW(process("kea-config-tool -l ethx -i -x all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Aggressivity) { + CommandOptions& opt = CommandOptions::instance(); + process("kea-config-tool -a 10 -l 192.168.0.1 all"); + EXPECT_EQ(10, opt.getAggressivity()); + + // Negative test cases + // Aggressivity must be non negative integer + EXPECT_THROW(process("kea-config-tool -l ethx -a 0 all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -l ethx -a all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -a -2 -l ethx -a 3 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, MaxDrop) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -D 25 -l ethx -r 10 all")); + EXPECT_EQ(25, opt.getMaxDrop()[0]); + EXPECT_NO_THROW(process("kea-config-tool -D 25 -l ethx -D 15 -r 10 all")); + EXPECT_EQ(25, opt.getMaxDrop()[0]); + EXPECT_EQ(15, opt.getMaxDrop()[1]); + + EXPECT_NO_THROW(process("kea-config-tool -D 15% -l ethx -r 10 all")); + EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); + EXPECT_NO_THROW(process("kea-config-tool -D 15% -D25% -l ethx -r 10 all")); + EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); + EXPECT_EQ(25, opt.getMaxDropPercentage()[1]); + EXPECT_NO_THROW(process("kea-config-tool -D 1% -D 99% -l ethx -r 10 all")); + EXPECT_EQ(1, opt.getMaxDropPercentage()[0]); + EXPECT_EQ(99, opt.getMaxDropPercentage()[1]); + + // Negative test cases + // Too many -D options + EXPECT_THROW(process("kea-config-tool -D 0% -D 1 -l ethx -r20 -D 3 all"), + isc::InvalidParameter); + // Too many -D options + EXPECT_THROW(process("kea-config-tool -D 99% -D 13% -l ethx -r20 -D 10% all"), + isc::InvalidParameter); + // Percentage is out of bounds + EXPECT_THROW(process("kea-config-tool -D101% -D 13% -l ethx -r20 all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -D0% -D 13% -l ethx -r20 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, NumRequest) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -n 1000 -r 10 -l ethx all")); + EXPECT_EQ(1000, opt.getNumRequests()[0]); + EXPECT_NO_THROW(process("kea-config-tool -n 5 -r 10 -n 500 -l ethx all")); + EXPECT_EQ(5, opt.getNumRequests()[0]); + EXPECT_EQ(500, opt.getNumRequests()[1]); + + // Negative test cases + // Too many -n parameters, expected maximum 2 + EXPECT_THROW(process("kea-config-tool -n 1 -n 2 -l ethx -n3 -r 20 all"), + isc::InvalidParameter); + // Num request must be positive integer + EXPECT_THROW(process("kea-config-tool -n 1 -n -22 -l ethx -r 10 all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -n 0 -l ethx -r 10 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Period) { + CommandOptions& opt = CommandOptions::instance(); + EXPECT_NO_THROW(process("kea-config-tool -p 120 -l ethx -r 100 all")); + EXPECT_EQ(120, opt.getPeriod()); + + // Negative test cases + // Test period must be positive integer + EXPECT_THROW(process("kea-config-tool -p 0 -l ethx -r 50 all"), + isc::InvalidParameter); + EXPECT_THROW(process("kea-config-tool -p -3 -l ethx -r 50 all"), + isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Interface) { + // In order to make this test portable we need to know + // at least one interface name on OS where test is run. + // Interface Manager has ability to detect interfaces. + // Although we don't call initIsInterface explicitly + // here it is called by CommandOptions object internally + // so this function is covered by the test. + dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance(); + const dhcp::IfaceMgr::IfaceCollection& ifaces = iface_mgr.getIfaces(); + std::string iface_name; + CommandOptions& opt = CommandOptions::instance(); + // The local loopback interface should be available. + // If no interface have been found for any reason we should + // not fail this test. + if (!ifaces.empty()) { + // Get the name of the interface we detected. + iface_name = (*ifaces.begin())->getName(); + // Use the name in the command parser. + ASSERT_NO_THROW(process("kea-config-tool -4 -l " + iface_name + " abc")); + // We expect that command parser will detect that argument + // specified along with '-l' is the interface name. + EXPECT_TRUE(opt.isInterface()); + + // If neither interface nor server is specified then + // exception is expected to be thrown. + EXPECT_THROW(process("kea-config-tool -4"), isc::InvalidParameter); + } +} + +TEST_F(CommandOptionsTest, Server) { + CommandOptions& opt = CommandOptions::instance(); + // There is at least server parameter needed. If server is not + // specified the local interface must be specified. + // The server value equal to 'all' means use broadcast. + ASSERT_NO_THROW(process("kea-config-tool all")); + // Once command line is parsed we expect that server name is + // set to broadcast address because 'all' was specified. + EXPECT_TRUE(opt.isBroadcast()); + // The broadcast address is 255.255.255.255. + EXPECT_EQ(DHCP_IPV4_BROADCAST_ADDRESS, opt.getServerName()); + + // When all is specified for DHCPv6 mode we expect + // FF02::1:2 as a server name which means All DHCP + // servers and relay agents in local network segment + ASSERT_NO_THROW(process("kea-config-tool -6 all")); + EXPECT_EQ(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, opt.getServerName()); + + // When server='servers' in DHCPv6 mode we expect + // FF05::1:3 as server name which means All DHCP + // servers in local network. + ASSERT_NO_THROW(process("kea-config-tool -6 servers")); + EXPECT_EQ(ALL_DHCP_SERVERS, opt.getServerName()); + + // If server name is neither 'all' nor 'servers' + // the given argument value is expected to be + // returned. + ASSERT_NO_THROW(process("kea-config-tool -6 abc")); + EXPECT_EQ("abc", opt.getServerName()); +} +//*/ diff --git a/src/bin/kea_config_tool/tests/config_tool_controller_unittest.cc b/src/bin/kea_config_tool/tests/config_tool_controller_unittest.cc new file mode 100644 index 0000000000..a436f52185 --- /dev/null +++ b/src/bin/kea_config_tool/tests/config_tool_controller_unittest.cc @@ -0,0 +1,21 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +// TODO! diff --git a/src/bin/kea_config_tool/tests/kea_admin_unittest.cc b/src/bin/kea_config_tool/tests/kea_admin_unittest.cc new file mode 100644 index 0000000000..a436f52185 --- /dev/null +++ b/src/bin/kea_config_tool/tests/kea_admin_unittest.cc @@ -0,0 +1,21 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +// TODO! diff --git a/src/bin/kea_config_tool/tests/run_unittests.cc b/src/bin/kea_config_tool/tests/run_unittests.cc new file mode 100644 index 0000000000..670c2bf747 --- /dev/null +++ b/src/bin/kea_config_tool/tests/run_unittests.cc @@ -0,0 +1,24 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return (isc::util::unittests::run_all()); +} diff --git a/src/bin/kea_config_tool/tests/server_config_unittest.cc b/src/bin/kea_config_tool/tests/server_config_unittest.cc new file mode 100644 index 0000000000..a436f52185 --- /dev/null +++ b/src/bin/kea_config_tool/tests/server_config_unittest.cc @@ -0,0 +1,21 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +// TODO! diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc index 8c18da45e8..97912ce71e 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc +++ b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc @@ -8,6 +8,8 @@ // mangling that accompanies use of the C++ compiler as well as to avoid // issues related to namespaces. +#include + #include #include #include diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc index 8215a6effa..7622a9c89d 100644 --- a/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc +++ b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include namespace isc { diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc index 535fc9803a..04b3252695 100644 --- a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc +++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc index 600d522379..4c87f80c8a 100644 --- a/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc +++ b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc index dae8074066..935b70ae59 100644 --- a/src/lib/asiolink/io_address.cc +++ b/src/lib/asiolink/io_address.cc @@ -173,5 +173,5 @@ IOAddress::increase(const IOAddress& addr) { } -} // namespace asiolink -} // namespace isc +} // namespace asiolink +} // namespace isc diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h index 42f09d7a9f..77af1369e3 100644 --- a/src/lib/asiolink/io_address.h +++ b/src/lib/asiolink/io_address.h @@ -339,6 +339,6 @@ class IOAddress { std::ostream& operator<<(std::ostream& os, const IOAddress& address); -} // namespace asiolink -} // namespace isc +} // namespace asiolink +} // namespace isc #endif // IO_ADDRESS_H diff --git a/src/lib/asiolink/testutils/test_server_unix_socket.cc b/src/lib/asiolink/testutils/test_server_unix_socket.cc index c72b1270d7..02d30bb5e4 100644 --- a/src/lib/asiolink/testutils/test_server_unix_socket.cc +++ b/src/lib/asiolink/testutils/test_server_unix_socket.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/asiolink/unix_domain_socket.cc b/src/lib/asiolink/unix_domain_socket.cc index af6f4cd9ba..d32684f067 100644 --- a/src/lib/asiolink/unix_domain_socket.cc +++ b/src/lib/asiolink/unix_domain_socket.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/cc/cfg_to_element.h b/src/lib/cc/cfg_to_element.h index 480dd1fa74..341e5c313c 100644 --- a/src/lib/cc/cfg_to_element.h +++ b/src/lib/cc/cfg_to_element.h @@ -42,7 +42,7 @@ struct CfgToElement { virtual isc::data::ElementPtr toElement() const = 0; }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace isc::dhcp +} // namespace isc #endif // CFG_TO_ELEMENT_H diff --git a/src/lib/cc/dhcp_config_error.h b/src/lib/cc/dhcp_config_error.h index 1337dd8afe..f7c989aa20 100644 --- a/src/lib/cc/dhcp_config_error.h +++ b/src/lib/cc/dhcp_config_error.h @@ -66,7 +66,7 @@ class DhcpConfigError : public isc::Exception { : isc::Exception(file, line, what) {} }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // end of isc::dhcp namespace +} // end of isc namespace #endif // DHCP_CONFIG_ERROR_H diff --git a/src/lib/cc/json_feed.cc b/src/lib/cc/json_feed.cc index 269d896c3a..3ea8040615 100644 --- a/src/lib/cc/json_feed.cc +++ b/src/lib/cc/json_feed.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/cc/simple_parser.cc b/src/lib/cc/simple_parser.cc index a8defad781..38b5e1368d 100644 --- a/src/lib/cc/simple_parser.cc +++ b/src/lib/cc/simple_parser.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/cc/simple_parser.h b/src/lib/cc/simple_parser.h index a825d769af..4a231af5b4 100644 --- a/src/lib/cc/simple_parser.h +++ b/src/lib/cc/simple_parser.h @@ -259,7 +259,7 @@ class SimpleParser { } }; -}; -}; +} +} #endif diff --git a/src/lib/cc/user_context.cc b/src/lib/cc/user_context.cc index e4e2399198..74104006ef 100644 --- a/src/lib/cc/user_context.cc +++ b/src/lib/cc/user_context.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include using namespace isc::data; diff --git a/src/lib/config/base_command_mgr.cc b/src/lib/config/base_command_mgr.cc index 2bca336174..17d058a804 100644 --- a/src/lib/config/base_command_mgr.cc +++ b/src/lib/config/base_command_mgr.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/config/client_connection.cc b/src/lib/config/client_connection.cc index 2045d263e4..e140568443 100644 --- a/src/lib/config/client_connection.cc +++ b/src/lib/config/client_connection.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/config/config_log.cc b/src/lib/config/config_log.cc index b1ad13df15..15c599ab08 100644 --- a/src/lib/config/config_log.cc +++ b/src/lib/config/config_log.cc @@ -14,7 +14,8 @@ namespace isc { namespace config { isc::log::Logger command_logger("commands"); +const int DBG_COMMAND = isc::log::DBGLVL_COMMAND; -} // namespace nsas -} // namespace isc +} // namespace nsas +} // namespace isc diff --git a/src/lib/config/config_log.h b/src/lib/config/config_log.h index 4937f4a9dc..e3dc18d893 100644 --- a/src/lib/config/config_log.h +++ b/src/lib/config/config_log.h @@ -21,9 +21,9 @@ namespace config { extern isc::log::Logger command_logger; // Enumerate configuration elements as they are processed. -const int DBG_COMMAND = isc::log::DBGLVL_COMMAND; +extern const int DBG_COMMAND; -} // namespace config -} // namespace isc +} // namespace config +} // namespace isc #endif // CONFIG_LOG_H diff --git a/src/lib/config/hooked_command_mgr.cc b/src/lib/config/hooked_command_mgr.cc index c7c2e3df2b..9238ed9975 100644 --- a/src/lib/config/hooked_command_mgr.cc +++ b/src/lib/config/hooked_command_mgr.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcp/classify.h b/src/lib/dhcp/classify.h index 14614313fc..2ddf72e49f 100644 --- a/src/lib/dhcp/classify.h +++ b/src/lib/dhcp/classify.h @@ -101,6 +101,13 @@ namespace dhcp { set_.clear(); } + /// @brief Removes an element from both containers. + void erase(const ClientClass& class_name) { + auto it = std::find(list_.begin(), list_.end(), class_name); + list_.erase(it); + set_.erase(class_name); + } + /// @brief Returns all class names as text /// /// @param separator Separator to be used between class names. The @@ -116,8 +123,8 @@ namespace dhcp { std::unordered_set set_; }; -}; +} -}; +} #endif /* CLASSIFY_H */ diff --git a/src/lib/dhcp/docsis3_option_defs.h b/src/lib/dhcp/docsis3_option_defs.h index 879ffd56ec..fe66d04ebd 100644 --- a/src/lib/dhcp/docsis3_option_defs.h +++ b/src/lib/dhcp/docsis3_option_defs.h @@ -25,7 +25,8 @@ const OptionDefParams DOCSIS3_V4_DEFS[] = { }; /// Number of option definitions defined. -const int DOCSIS3_V4_DEFS_SIZE = sizeof(DOCSIS3_V4_DEFS) / sizeof(OptionDefParams); +const int DOCSIS3_V4_DEFS_SIZE = + sizeof(DOCSIS3_V4_DEFS) / sizeof(DOCSIS3_V4_DEFS[0]); /// @todo define remaining docsis3 v6 codes #define DOCSIS3_V6_ORO 1 @@ -65,7 +66,7 @@ const int DOCSIS3_V6_DEFS_SIZE = extern const char* DOCSIS3_CLASS_EROUTER; extern const char* DOCSIS3_CLASS_MODEM; -}; // isc::dhcp namespace -}; // isc namespace +} // namespace dhcp +} // namespace isc #endif // DOCSIS3_OPTION_DEFS_H diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h index 1b4e42bd6c..9df3033497 100644 --- a/src/lib/dhcp/duid.h +++ b/src/lib/dhcp/duid.h @@ -167,7 +167,7 @@ class ClientId : public DUID { bool operator!=(const ClientId& other) const; }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // end of isc::dhcp namespace +} // end of isc namespace #endif /* DUID_H */ diff --git a/src/lib/dhcp/hwaddr.h b/src/lib/dhcp/hwaddr.h index 8e98470744..75c3f83219 100644 --- a/src/lib/dhcp/hwaddr.h +++ b/src/lib/dhcp/hwaddr.h @@ -153,7 +153,7 @@ struct HWAddr { /// @brief Shared pointer to a hardware address structure typedef boost::shared_ptr HWAddrPtr; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc #endif // HWADDR_H diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 7d06326c0c..edeb38b0db 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -172,9 +172,8 @@ IfaceMgr::IfaceMgr() control_buf_(new char[control_buf_len_]), packet_filter_(new PktFilterInet()), packet_filter6_(new PktFilterInet6()), - test_mode_(false), - allow_loopback_(false) -{ + test_mode_(false), server_mode_(true), + allow_loopback_(false) { try { // required for sending/receiving packets @@ -1193,5 +1192,5 @@ IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) { return (*candidate); } -} // end of namespace isc::dhcp -} // end of namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 42b5037664..3f2845d6b7 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -595,6 +595,20 @@ class IfaceMgr : public boost::noncopyable { return (test_mode_); } + /// @param server_mode A flag which indicates that the @c IfaceMgr is initialized + /// in the server daemon (if true), or not (if false). + void setServerMode(const bool server_mode) { + server_mode_ = server_mode; + } + + /// @brief Checks if the @c IfaceMgr is initialized in the server daemon. + /// + /// @return true if the @c IfaceMgr is initialized in the server daemon, + /// false otherwise. + bool isServerMode() const { + return (server_mode_); + } + /// @brief Allows or disallows the loopback interface /// /// By default the loopback interface is not considered when opening @@ -1227,11 +1241,14 @@ class IfaceMgr : public boost::noncopyable { /// @brief Indicates if the IfaceMgr is in the test mode. bool test_mode_; + /// @brief Indicates if the IfaceMgr is initialized in the server daemon. + bool server_mode_; + /// @brief Allows to use loopback bool allow_loopback_; }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // IFACE_MGR_H diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index 330c02b140..8a9bc60b4f 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -778,7 +778,7 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe options.insert(std::make_pair(opt_type, opt)); offset += opt_len; - } // end of data-chunk + } // end of data-chunk break; // end of the vendor block. } diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc index 68f4d23d8b..02e6fa0b47 100644 --- a/src/lib/dhcp/option_custom.cc +++ b/src/lib/dhcp/option_custom.cc @@ -113,7 +113,7 @@ OptionCustom::addArrayDataField(const PSIDLen& psid_len, const PSID& psid) { checkArrayType(); if (definition_.getType() != OPT_PSID_TYPE) { - isc_throw(BadDataTypeCast, "PSID value can be specified onlu for" + isc_throw(BadDataTypeCast, "PSID value can be specified only for" " an option comprising an array of PSID length / value" " tuples"); } diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h index 358dcc0728..d00f1ed6d9 100644 --- a/src/lib/dhcp/option_data_types.h +++ b/src/lib/dhcp/option_data_types.h @@ -661,7 +661,7 @@ class OptionDataTypeUtil { }; -} // isc::dhcp namespace -} // isc namespace +} // namespace dhcp +} // namespace isc #endif // OPTION_DATA_TYPES_H diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index 4f77621a33..6da4cedc48 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -662,9 +662,9 @@ OptionDefinition::writeToBuffer(Option::Universe u, OptionDataTypeUtil::writePrefix(PrefixLen(len), address, buf); return; - } + } case OPT_PSID_TYPE: - { + { std::string txt = value; // first let's remove any whitespaces diff --git a/src/lib/dhcp/option_space.h b/src/lib/dhcp/option_space.h index 96c401d184..707fd535cd 100644 --- a/src/lib/dhcp/option_space.h +++ b/src/lib/dhcp/option_space.h @@ -183,7 +183,7 @@ class OptionSpace6 : public OptionSpace { uint32_t enterprise_number_; ///< IANA assigned enterprise number. }; -} // namespace isc::dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // OPTION_SPACE_H diff --git a/src/lib/dhcp/pkt.cc b/src/lib/dhcp/pkt.cc index cc7c2739cc..c06a92b789 100644 --- a/src/lib/dhcp/pkt.cc +++ b/src/lib/dhcp/pkt.cc @@ -278,5 +278,5 @@ Pkt::getMACFromIPv6(const isc::asiolink::IOAddress& addr) { return (mac); } -}; -}; +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcp/pkt.h b/src/lib/dhcp/pkt.h index f2efc14de8..8592427135 100644 --- a/src/lib/dhcp/pkt.h +++ b/src/lib/dhcp/pkt.h @@ -19,7 +19,6 @@ #include namespace isc { - namespace dhcp { /// @brief RAII object enabling copying options retrieved from the @@ -784,7 +783,7 @@ class Pkt { HWAddrPtr& storage); }; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc index 007e3f91fc..6f38591129 100644 --- a/src/lib/dhcp/pkt4.cc +++ b/src/lib/dhcp/pkt4.cc @@ -562,6 +562,6 @@ Pkt4::isRelayed() const { return (!giaddr_.isV4Zero() && !giaddr_.isV4Bcast()); } -} // end of namespace isc::dhcp +} // namespace dhcp -} // end of namespace isc +} // namespace isc diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h index 26ded314f1..d33d500db5 100644 --- a/src/lib/dhcp/pkt4.h +++ b/src/lib/dhcp/pkt4.h @@ -540,13 +540,13 @@ class Pkt4 : public Pkt { uint8_t file_[MAX_FILE_LEN]; // end of real DHCPv4 fields -}; // Pkt4 class +}; // Pkt4 class /// @brief A pointer to Pkt4 object. typedef boost::shared_ptr Pkt4Ptr; -} // isc::dhcp namespace +} // namespace dhcp -} // isc namespace +} // namespace isc #endif diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index c097d250db..4594cd1977 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -268,7 +268,8 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = { /// Number of option definitions defined. const int STANDARD_V4_OPTION_DEFINITIONS_SIZE = - sizeof(STANDARD_V4_OPTION_DEFINITIONS) / sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]); + sizeof(STANDARD_V4_OPTION_DEFINITIONS) / + sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]); /// Last resort definitions (only option 43 for now, these definitions /// are applied in deferred unpacking when none is found). @@ -277,7 +278,9 @@ const OptionDefParams LAST_RESORT_V4_OPTION_DEFINITIONS[] = { OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "vendor-encapsulated-options-space" } }; -const int LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE = 1; +const int LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE = + sizeof(LAST_RESORT_V4_OPTION_DEFINITIONS) / + sizeof(LAST_RESORT_V4_OPTION_DEFINITIONS[0]); /// Start Definition of DHCPv6 options @@ -548,9 +551,9 @@ const int V4V6_BIND_OPTION_DEFINITIONS_SIZE = sizeof(V4V6_BIND_OPTION_DEFINITIONS) / sizeof(V4V6_BIND_OPTION_DEFINITIONS[0]); -} // unnamed namespace +} // namespace -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // STD_OPTION_DEFS_H diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index 8462cf33ba..798ea7988f 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -2156,4 +2156,4 @@ TEST_F(LibDhcpTest, sw46options) { EXPECT_EQ("type=00093, len=00004: 8 (uint8) len=6,psid=63 (psid)", portparam->toText()); } -} // end of anonymous space +} // end of anonymous space diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc index e0311e7c69..43a8025435 100644 --- a/src/lib/dhcp/tests/option_custom_unittest.cc +++ b/src/lib/dhcp/tests/option_custom_unittest.cc @@ -2489,4 +2489,4 @@ TEST_F(OptionCustomTest, toTextNoRecord) { option.toText()); } -} // anonymous namespace +} // namespace diff --git a/src/lib/dhcp/tests/option_data_types_unittest.cc b/src/lib/dhcp/tests/option_data_types_unittest.cc index f90ebec723..d74ea5cd93 100644 --- a/src/lib/dhcp/tests/option_data_types_unittest.cc +++ b/src/lib/dhcp/tests/option_data_types_unittest.cc @@ -874,4 +874,4 @@ TEST_F(OptionDataTypesTest, writeString) { EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin())); } -} // anonymous namespace +} // namespace diff --git a/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc b/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc index 6bbd76660f..8719846c3a 100644 --- a/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc +++ b/src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc @@ -177,7 +177,7 @@ TEST(OptionOpaqueDataTuples, unpack6) { // is correctly parsed. TEST(OptionOpaqueDataTuples, unpack6EmptyTuple) { // Prepare data to decode. - const uint8_t buf_data[] = {0x00, 0x00}; // tuple length is 0 + const uint8_t buf_data[] = {0x00, 0x00}; // tuple length is 0 OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); OptionOpaqueDataTuplesPtr data_tuple; @@ -210,10 +210,7 @@ TEST(OptionOpaqueDataTuples, unpack6Truncated) { // This test checks that the DHCPv6 bootfile-param option containing no opaque // data is parsed correctly. TEST(OptionOpaqueDataTuples, unpack6NoTuple) { - // Prepare data to decode. - const uint8_t buf_data[] = { - }; - OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + OptionBuffer buf; OptionOpaqueDataTuplesPtr data_tuple; ASSERT_NO_THROW( @@ -250,4 +247,4 @@ TEST(OptionOpaqueDataTuples, toText6) { data_tuple.toText(2)); } -} // end of anonymous namespace +} // namespace diff --git a/src/lib/dhcp_ddns/ncr_msg.h b/src/lib/dhcp_ddns/ncr_msg.h index 958f57509d..56147eaf58 100644 --- a/src/lib/dhcp_ddns/ncr_msg.h +++ b/src/lib/dhcp_ddns/ncr_msg.h @@ -722,7 +722,7 @@ class NameChangeRequest { }; -}; // end of isc::dhcp_ddns namespace -}; // end of isc namespace +} // end of isc::dhcp_ddns namespace +} // end of isc namespace #endif diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index d14933e4f0..43c3a77136 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -106,6 +106,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_shared_networks.cc cfg_shared_networks.h libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h +libkea_dhcpsrv_la_SOURCES += cfg_srv_config_type.cc cfg_srv_config_type.h libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h libkea_dhcpsrv_la_SOURCES += client_class_def.cc client_class_def.h libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h @@ -131,6 +132,10 @@ libkea_dhcpsrv_la_SOURCES += lease_file_loader.h libkea_dhcpsrv_la_SOURCES += lease_file_stats.h libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h +libkea_dhcpsrv_la_SOURCES += srv_config_mgr.cc srv_config_mgr.h +libkea_dhcpsrv_la_SOURCES += srv_config_mgr_factory.cc srv_config_mgr_factory.h +libkea_dhcpsrv_la_SOURCES += srv_config_master_mgr.cc srv_config_master_mgr.h +libkea_dhcpsrv_la_SOURCES += srv_config_master_mgr_factory.cc srv_config_master_mgr_factory.h libkea_dhcpsrv_la_SOURCES += logging.cc logging.h libkea_dhcpsrv_la_SOURCES += logging_info.cc logging_info.h libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h @@ -141,6 +146,8 @@ if HAVE_MYSQL libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h libkea_dhcpsrv_la_SOURCES += mysql_connection.cc mysql_connection.h libkea_dhcpsrv_la_SOURCES += mysql_host_data_source.cc mysql_host_data_source.h +libkea_dhcpsrv_la_SOURCES += mysql_srv_config_mgr.cc mysql_srv_config_mgr.h +libkea_dhcpsrv_la_SOURCES += mysql_srv_config_master_mgr.cc mysql_srv_config_master_mgr.h endif libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h @@ -152,6 +159,8 @@ libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h libkea_dhcpsrv_la_SOURCES += pgsql_exchange.cc pgsql_exchange.h libkea_dhcpsrv_la_SOURCES += pgsql_host_data_source.cc pgsql_host_data_source.h libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h +libkea_dhcpsrv_la_SOURCES += pgsql_srv_config_mgr.cc pgsql_srv_config_mgr.h +libkea_dhcpsrv_la_SOURCES += pgsql_srv_config_master_mgr.cc pgsql_srv_config_master_mgr.h endif if HAVE_CQL @@ -159,6 +168,8 @@ libkea_dhcpsrv_la_SOURCES += cql_connection.cc cql_connection.h libkea_dhcpsrv_la_SOURCES += cql_exchange.cc cql_exchange.h libkea_dhcpsrv_la_SOURCES += cql_host_data_source.cc cql_host_data_source.h libkea_dhcpsrv_la_SOURCES += cql_lease_mgr.cc cql_lease_mgr.h +libkea_dhcpsrv_la_SOURCES += cql_srv_config_mgr.cc cql_srv_config_mgr.h +libkea_dhcpsrv_la_SOURCES += cql_srv_config_master_mgr.cc cql_srv_config_master_mgr.h endif libkea_dhcpsrv_la_SOURCES += pool.cc pool.h diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 2be4a8fd17..2ac0524bc1 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -79,7 +79,7 @@ struct AllocEngineHooks { // module is called. AllocEngineHooks Hooks; -}; // anonymous namespace +} // namespace namespace isc { namespace dhcp { @@ -1914,103 +1914,133 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); - // This value indicates if we have been able to deal with all expired - // leases in this pass. - bool incomplete_reclamation = false; - Lease6Collection leases; - // The value of 0 has a special meaning - reclaim all. - if (max_leases > 0) { - // If the value is non-zero, the caller has limited the number of - // leases to reclaim. We obtain one lease more to see if there will - // be still leases left after this pass. - lease_mgr.getExpiredLeases6(leases, max_leases + 1); - // There are more leases expired leases than we will process in this - // pass, so we should mark it as an incomplete reclamation. We also - // remove this extra lease (which we don't want to process anyway) - // from the collection. - if (leases.size() > max_leases) { - leases.pop_back(); - incomplete_reclamation = true; - } - - } else { - // If there is no limitation on the number of leases to reclaim, - // we will try to process all. Hence, we don't mark it as incomplete - // reclamation just yet. - lease_mgr.getExpiredLeases6(leases, max_leases); + bool transactionError = false; + try { + lease_mgr.startTransaction(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + transactionError = true; } + if (!transactionError) { + bool rollback = false; + try { - // Do not initialize the callout handle until we know if there are any - // lease6_expire callouts installed. - CalloutHandlePtr callout_handle; - if (!leases.empty() && - HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease6_expire_)) { - callout_handle = HooksManager::createCalloutHandle(); - } + // This value indicates if we have been able to deal with all expired + // leases in this pass. + bool incomplete_reclamation = false; + Lease6Collection leases; + // The value of 0 has a special meaning - reclaim all. + if (max_leases > 0) { + // If the value is non-zero, the caller has limited the number of + // leases to reclaim. We obtain one lease more to see if there will + // be still leases left after this pass. + lease_mgr.getExpiredLeases6(leases, max_leases + 1); + // There are more leases expired leases than we will process in this + // pass, so we should mark it as an incomplete reclamation. We also + // remove this extra lease (which we don't want to process anyway) + // from the collection. + if (leases.size() > max_leases) { + leases.pop_back(); + incomplete_reclamation = true; + } - size_t leases_processed = 0; - BOOST_FOREACH(Lease6Ptr lease, leases) { + } else { + // If there is no limitation on the number of leases to reclaim, + // we will try to process all. Hence, we don't mark it as incomplete + // reclamation just yet. + lease_mgr.getExpiredLeases6(leases, max_leases); + } - try { - // Reclaim the lease. - reclaimExpiredLease(lease, remove_lease, callout_handle); - ++leases_processed; + // Do not initialize the callout handle until we know if there are any + // lease6_expire callouts installed. + CalloutHandlePtr callout_handle; + if (!leases.empty() && + HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease6_expire_)) { + callout_handle = HooksManager::createCalloutHandle(); + } - } catch (const std::exception& ex) { - LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED) - .arg(lease->addr_.toText()) - .arg(ex.what()); - } + size_t leases_processed = 0; + BOOST_FOREACH(Lease6Ptr lease, leases) { - // Check if we have hit the timeout for running reclamation routine and - // return if we have. We're checking it here, because we always want to - // allow reclaiming at least one lease. - if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) { - // Timeout. This will likely mean that we haven't been able to process - // all leases we wanted to process. The reclamation pass will be - // probably marked as incomplete. - if (!incomplete_reclamation) { - if (leases_processed < leases.size()) { - incomplete_reclamation = true; + try { + // Reclaim the lease. + reclaimExpiredLease(lease, remove_lease, callout_handle); + ++leases_processed; + + } catch (const std::exception& ex) { + LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED) + .arg(lease->addr_.toText()) + .arg(ex.what()); + rollback = true; + } + + // Check if we have hit the timeout for running reclamation routine and + // return if we have. We're checking it here, because we always want to + // allow reclaiming at least one lease. + if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) { + // Timeout. This will likely mean that we haven't been able to process + // all leases we wanted to process. The reclamation pass will be + // probably marked as incomplete. + if (!incomplete_reclamation) { + if (leases_processed < leases.size()) { + incomplete_reclamation = true; + } + } + + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V6_LEASES_RECLAMATION_TIMEOUT) + .arg(timeout); + break; } } + // Stop measuring the time. + stopwatch.stop(); + + // Mark completion of the lease reclamation routine and present some stats. LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V6_LEASES_RECLAMATION_TIMEOUT) - .arg(timeout); - break; - } - } + ALLOC_ENGINE_V6_LEASES_RECLAMATION_COMPLETE) + .arg(leases_processed) + .arg(stopwatch.logFormatTotalDuration()); + + // Check if this was an incomplete reclamation and increase the number of + // consecutive incomplete reclamations. + if (incomplete_reclamation) { + ++incomplete_v6_reclamations_; + // If the number of incomplete reclamations is beyond the threshold, we + // need to issue a warning. + if ((max_unwarned_cycles > 0) && + (incomplete_v6_reclamations_ > max_unwarned_cycles)) { + LOG_WARN(alloc_engine_logger, ALLOC_ENGINE_V6_LEASES_RECLAMATION_SLOW) + .arg(max_unwarned_cycles); + // We issued a warning, so let's now reset the counter. + incomplete_v6_reclamations_ = 0; + } - // Stop measuring the time. - stopwatch.stop(); + } else { + // This was a complete reclamation, so let's reset the counter. + incomplete_v6_reclamations_ = 0; - // Mark completion of the lease reclamation routine and present some stats. - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V6_LEASES_RECLAMATION_COMPLETE) - .arg(leases_processed) - .arg(stopwatch.logFormatTotalDuration()); - - // Check if this was an incomplete reclamation and increase the number of - // consecutive incomplete reclamations. - if (incomplete_reclamation) { - ++incomplete_v6_reclamations_; - // If the number of incomplete reclamations is beyond the threshold, we - // need to issue a warning. - if ((max_unwarned_cycles > 0) && - (incomplete_v6_reclamations_ > max_unwarned_cycles)) { - LOG_WARN(alloc_engine_logger, ALLOC_ENGINE_V6_LEASES_RECLAMATION_SLOW) - .arg(max_unwarned_cycles); - // We issued a warning, so let's now reset the counter. - incomplete_v6_reclamations_ = 0; + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V6_NO_MORE_EXPIRED_LEASES); + } + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + rollback = true; + } + if (rollback) { + try { + lease_mgr.rollback(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } + } else { + try { + lease_mgr.commit(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } } - - } else { - // This was a complete reclamation, so let's reset the counter. - incomplete_v6_reclamations_ = 0; - - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V6_NO_MORE_EXPIRED_LEASES); } } @@ -2019,21 +2049,44 @@ AllocEngine::deleteExpiredReclaimedLeases6(const uint32_t secs) { LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE) .arg(secs); - - uint64_t deleted_leases = 0; + bool transactionError = false; try { - // Try to delete leases from the lease database. - LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); - deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs); - + LeaseMgrFactory::instance().startTransaction(); } catch (const std::exception& ex) { - LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED) - .arg(ex.what()); + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + transactionError = true; } + if (!transactionError) { + bool rollback = false; + uint64_t deleted_leases = 0; + try { + // Try to delete leases from the lease database. + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs); - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_COMPLETE) - .arg(deleted_leases); + } catch (const std::exception& ex) { + LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED) + .arg(ex.what()); + rollback = true; + } + + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_COMPLETE) + .arg(deleted_leases); + if (rollback) { + try { + LeaseMgrFactory::instance().rollback(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } + } else { + try { + LeaseMgrFactory::instance().commit(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } + } + } } @@ -2053,104 +2106,134 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); - // This value indicates if we have been able to deal with all expired - // leases in this pass. - bool incomplete_reclamation = false; - Lease4Collection leases; - // The value of 0 has a special meaning - reclaim all. - if (max_leases > 0) { - // If the value is non-zero, the caller has limited the number of - // leases to reclaim. We obtain one lease more to see if there will - // be still leases left after this pass. - lease_mgr.getExpiredLeases4(leases, max_leases + 1); - // There are more leases expired leases than we will process in this - // pass, so we should mark it as an incomplete reclamation. We also - // remove this extra lease (which we don't want to process anyway) - // from the collection. - if (leases.size() > max_leases) { - leases.pop_back(); - incomplete_reclamation = true; - } - - } else { - // If there is no limitation on the number of leases to reclaim, - // we will try to process all. Hence, we don't mark it as incomplete - // reclamation just yet. - lease_mgr.getExpiredLeases4(leases, max_leases); + bool transactionError = false; + try { + lease_mgr.startTransaction(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + transactionError = true; } + if (!transactionError) { + bool rollback = false; + try { + // This value indicates if we have been able to deal with all expired + // leases in this pass. + bool incomplete_reclamation = false; + Lease4Collection leases; + // The value of 0 has a special meaning - reclaim all. + if (max_leases > 0) { + // If the value is non-zero, the caller has limited the number of + // leases to reclaim. We obtain one lease more to see if there will + // be still leases left after this pass. + lease_mgr.getExpiredLeases4(leases, max_leases + 1); + // There are more leases expired leases than we will process in this + // pass, so we should mark it as an incomplete reclamation. We also + // remove this extra lease (which we don't want to process anyway) + // from the collection. + if (leases.size() > max_leases) { + leases.pop_back(); + incomplete_reclamation = true; + } - // Do not initialize the callout handle until we know if there are any - // lease4_expire callouts installed. - CalloutHandlePtr callout_handle; - if (!leases.empty() && - HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_expire_)) { - callout_handle = HooksManager::createCalloutHandle(); - } + } else { + // If there is no limitation on the number of leases to reclaim, + // we will try to process all. Hence, we don't mark it as incomplete + // reclamation just yet. + lease_mgr.getExpiredLeases4(leases, max_leases); + } - size_t leases_processed = 0; - BOOST_FOREACH(Lease4Ptr lease, leases) { - try { - // Reclaim the lease. - reclaimExpiredLease(lease, remove_lease, callout_handle); - ++leases_processed; + // Do not initialize the callout handle until we know if there are any + // lease4_expire callouts installed. + CalloutHandlePtr callout_handle; + if (!leases.empty() && + HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_expire_)) { + callout_handle = HooksManager::createCalloutHandle(); + } - } catch (const std::exception& ex) { - LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED) - .arg(lease->addr_.toText()) - .arg(ex.what()); - } + size_t leases_processed = 0; + BOOST_FOREACH(Lease4Ptr lease, leases) { - // Check if we have hit the timeout for running reclamation routine and - // return if we have. We're checking it here, because we always want to - // allow reclaiming at least one lease. - if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) { - // Timeout. This will likely mean that we haven't been able to process - // all leases we wanted to process. The reclamation pass will be - // probably marked as incomplete. - if (!incomplete_reclamation) { - if (leases_processed < leases.size()) { - incomplete_reclamation = true; + try { + // Reclaim the lease. + reclaimExpiredLease(lease, remove_lease, callout_handle); + ++leases_processed; + + } catch (const std::exception& ex) { + LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED) + .arg(lease->addr_.toText()) + .arg(ex.what()); + rollback = true; + } + + // Check if we have hit the timeout for running reclamation routine and + // return if we have. We're checking it here, because we always want to + // allow reclaiming at least one lease. + if ((timeout > 0) && (stopwatch.getTotalMilliseconds() >= timeout)) { + // Timeout. This will likely mean that we haven't been able to process + // all leases we wanted to process. The reclamation pass will be + // probably marked as incomplete. + if (!incomplete_reclamation) { + if (leases_processed < leases.size()) { + incomplete_reclamation = true; + } + } + + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V4_LEASES_RECLAMATION_TIMEOUT) + .arg(timeout); + break; } } + // Stop measuring the time. + stopwatch.stop(); + + // Mark completion of the lease reclamation routine and present some stats. LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V4_LEASES_RECLAMATION_TIMEOUT) - .arg(timeout); - break; - } - } + ALLOC_ENGINE_V4_LEASES_RECLAMATION_COMPLETE) + .arg(leases_processed) + .arg(stopwatch.logFormatTotalDuration()); + + // Check if this was an incomplete reclamation and increase the number of + // consecutive incomplete reclamations. + if (incomplete_reclamation) { + ++incomplete_v4_reclamations_; + // If the number of incomplete reclamations is beyond the threshold, we + // need to issue a warning. + if ((max_unwarned_cycles > 0) && + (incomplete_v4_reclamations_ > max_unwarned_cycles)) { + LOG_WARN(alloc_engine_logger, ALLOC_ENGINE_V4_LEASES_RECLAMATION_SLOW) + .arg(max_unwarned_cycles); + // We issued a warning, so let's now reset the counter. + incomplete_v4_reclamations_ = 0; + } - // Stop measuring the time. - stopwatch.stop(); + } else { + // This was a complete reclamation, so let's reset the counter. + incomplete_v4_reclamations_ = 0; - // Mark completion of the lease reclamation routine and present some stats. - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V4_LEASES_RECLAMATION_COMPLETE) - .arg(leases_processed) - .arg(stopwatch.logFormatTotalDuration()); - - // Check if this was an incomplete reclamation and increase the number of - // consecutive incomplete reclamations. - if (incomplete_reclamation) { - ++incomplete_v4_reclamations_; - // If the number of incomplete reclamations is beyond the threshold, we - // need to issue a warning. - if ((max_unwarned_cycles > 0) && - (incomplete_v4_reclamations_ > max_unwarned_cycles)) { - LOG_WARN(alloc_engine_logger, ALLOC_ENGINE_V4_LEASES_RECLAMATION_SLOW) - .arg(max_unwarned_cycles); - // We issued a warning, so let's now reset the counter. - incomplete_v4_reclamations_ = 0; + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V4_NO_MORE_EXPIRED_LEASES); + } + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + rollback = true; + } + if (rollback) { + try { + lease_mgr.rollback(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } + } else { + try { + lease_mgr.commit(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } } - - } else { - // This was a complete reclamation, so let's reset the counter. - incomplete_v4_reclamations_ = 0; - - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V4_NO_MORE_EXPIRED_LEASES); } } @@ -2350,20 +2433,44 @@ AllocEngine::deleteExpiredReclaimedLeases4(const uint32_t secs) { ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE) .arg(secs); - uint64_t deleted_leases = 0; + bool transactionError = false; try { - // Try to delete leases from the lease database. - LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); - deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs); - + LeaseMgrFactory::instance().startTransaction(); } catch (const std::exception& ex) { - LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED) - .arg(ex.what()); + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + transactionError = true; } + if (!transactionError) { + bool rollback = false; + uint64_t deleted_leases = 0; + try { + // Try to delete leases from the lease database. + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs); - LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, - ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_COMPLETE) - .arg(deleted_leases); + } catch (const std::exception& ex) { + LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED) + .arg(ex.what()); + rollback = true; + } + + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_COMPLETE) + .arg(deleted_leases); + if (rollback) { + try { + LeaseMgrFactory::instance().rollback(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } + } else { + try { + LeaseMgrFactory::instance().commit(); + } catch (const std::exception& ex) { + LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_TRANSACTION_FAILED).arg(ex.what()); + } + } + } } bool @@ -2517,8 +2624,8 @@ void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease, } -} // end of isc::dhcp namespace -} // end of isc namespace +} // namespace dhcp +} // namespace isc // ########################################################################## // # DHCPv4 lease allocation code starts here. @@ -2727,7 +2834,7 @@ inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) { return (false); } -} // end of anonymous namespace +} // namespace namespace isc { namespace dhcp { @@ -3479,7 +3586,6 @@ AllocEngine::reuseExpiredLease4(Lease4Ptr& expired, Lease4Ptr AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& ctx) { ctx.conflicting_lease_.reset(); - Lease4Ptr exist_lease = LeaseMgrFactory::instance().getLease4(candidate); if (exist_lease) { if (exist_lease->expired()) { @@ -3607,5 +3713,5 @@ AllocEngine::conditionalExtendLifetime(Lease& lease) const { return (true); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h index 4b1dce5c73..e6cd545197 100644 --- a/src/lib/dhcpsrv/alloc_engine.h +++ b/src/lib/dhcpsrv/alloc_engine.h @@ -149,8 +149,8 @@ class AllocEngine : public boost::noncopyable { /// increased by prefix length /32 will become 2001:db9::. This method /// is used to iterate over IPv6 prefix pools /// - /// @param prefix prefix to be increased - /// @param prefix_len length of the prefix to be increased + /// @param prefix prefix being increased + /// @param prefix_len length of the prefix being increased /// @return result prefix static isc::asiolink::IOAddress increasePrefix(const isc::asiolink::IOAddress& prefix, @@ -379,7 +379,6 @@ class AllocEngine : public boost::noncopyable { /// @brief A collection of newly allocated leases. Lease6Collection new_leases_; - //@} /// @brief Parameters pertaining to individual IAs. @@ -1523,7 +1522,7 @@ class AllocEngine : public boost::noncopyable { /// @brief A pointer to the @c AllocEngine object. typedef boost::shared_ptr AllocEnginePtr; -}; // namespace isc::dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // ALLOC_ENGINE_H diff --git a/src/lib/dhcpsrv/alloc_engine_log.cc b/src/lib/dhcpsrv/alloc_engine_log.cc index 3d5fa15d60..eb3e93c92e 100644 --- a/src/lib/dhcpsrv/alloc_engine_log.cc +++ b/src/lib/dhcpsrv/alloc_engine_log.cc @@ -13,8 +13,13 @@ namespace isc { namespace dhcp { +const int ALLOC_ENGINE_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +const int ALLOC_ENGINE_DBG_RESULTS = isc::log::DBGLVL_TRACE_BASIC_DATA; +const int ALLOC_ENGINE_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +const int ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; + isc::log::Logger alloc_engine_logger("alloc-engine"); -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/alloc_engine_log.h b/src/lib/dhcpsrv/alloc_engine_log.h index 5c35a9eca3..d1b14836a6 100644 --- a/src/lib/dhcpsrv/alloc_engine_log.h +++ b/src/lib/dhcpsrv/alloc_engine_log.h @@ -19,23 +19,23 @@ namespace dhcp { /// Defines the levels used to output debug messages from the @c AllocEngine. /// @brief Traces normal operations -const int ALLOC_ENGINE_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +extern const int ALLOC_ENGINE_DBG_TRACE; /// @brief Records the results of various operations. /// /// Messages logged at this level will typically contain summary of the /// data retrieved. -const int ALLOC_ENGINE_DBG_RESULTS = isc::log::DBGLVL_TRACE_BASIC_DATA; +extern const int ALLOC_ENGINE_DBG_RESULTS; /// @brief Record detailed traces /// /// Messages logged at this level will log detailed tracing information. -const int ALLOC_ENGINE_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +extern const int ALLOC_ENGINE_DBG_TRACE_DETAIL; /// @brief Records detailed results of various operations. /// /// Messages logged at this level will contain detailed results. -const int ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; +extern const int ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA; //@} @@ -44,7 +44,7 @@ const int ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DAT /// Define the logger used to log messages in @c AllocEngine. extern isc::log::Logger alloc_engine_logger; -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // ALLOC_ENGINE_LOG_H diff --git a/src/lib/dhcpsrv/alloc_engine_messages.mes b/src/lib/dhcpsrv/alloc_engine_messages.mes index 64bf7bed7e..6fbfa8d900 100644 --- a/src/lib/dhcpsrv/alloc_engine_messages.mes +++ b/src/lib/dhcpsrv/alloc_engine_messages.mes @@ -455,3 +455,6 @@ This is a normal occurrence during conflict resolution, which can occur in cases such as the system administrator adding a reservation for an address that is currently in use by another client. The server will fully recover from this situation, but clients will change their prefixes. + +% ALLOC_ENGINE_TRANSACTION_FAILED transaction failed. error: %1 +The transaction failed with the error message. diff --git a/src/lib/dhcpsrv/base_host_data_source.h b/src/lib/dhcpsrv/base_host_data_source.h index 640715ca31..0c0eab7782 100644 --- a/src/lib/dhcpsrv/base_host_data_source.h +++ b/src/lib/dhcpsrv/base_host_data_source.h @@ -11,7 +11,10 @@ #include #include #include +#include #include +#include + #include #include @@ -308,6 +311,12 @@ class BaseHostDataSource { /// Rolls back all pending database operations. On databases that don't /// support transactions, this is a no-op. virtual void rollback() {}; + + /// @brief Sync database tables with kea.conf + /// + /// Truncates database tables, retrieves reservations read from kea.conf and + /// inserts coresponding hosts, reservations and options in the database. + virtual void syncReservations() {}; }; /// @brief HostDataSource pointer @@ -316,7 +325,7 @@ typedef boost::shared_ptr HostDataSourcePtr; /// @brief HostDataSource list typedef std::vector HostDataSourceList; -} -} +} // namespace dhcp +} // namespace isc #endif // BASE_HOST_DATA_SOURCE_H diff --git a/src/lib/dhcpsrv/benchmarks/cql_host_data_source_benchmark.cc b/src/lib/dhcpsrv/benchmarks/cql_host_data_source_benchmark.cc index f0223012be..4ae4318418 100644 --- a/src/lib/dhcpsrv/benchmarks/cql_host_data_source_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/cql_host_data_source_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -41,13 +42,13 @@ class CqlHostDataSourceBenchmark : public GenericHostDataSourceBenchmark { destroyCqlSchema(false, true); createCqlSchema(false, true); try { - HostDataSourceFactory::destroy(); - HostDataSourceFactory::create(validCqlConnectionString()); + HostMgr::delBackend("cql"); + HostMgr::addBackend(validCqlConnectionString()); } catch (...) { cerr << "ERROR: unable to open database" << endl; throw; } - hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr(); + hdsptr_ = HostMgr::instance().getHostDataSource(); } /// @brief Cleans up after the test. @@ -59,7 +60,7 @@ class CqlHostDataSourceBenchmark : public GenericHostDataSourceBenchmark { " is opened in read-only mode, continuing..." << endl; } - HostDataSourceFactory::destroy(); + HostMgr::delBackend("cql"); destroyCqlSchema(false, true); } }; diff --git a/src/lib/dhcpsrv/benchmarks/cql_lease_mgr_benchmark.cc b/src/lib/dhcpsrv/benchmarks/cql_lease_mgr_benchmark.cc index 2179f6415f..60cb0aa7b7 100644 --- a/src/lib/dhcpsrv/benchmarks/cql_lease_mgr_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/cql_lease_mgr_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // diff --git a/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.cc b/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.cc index ed944b0871..9029867e95 100644 --- a/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // diff --git a/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.h b/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.h index 8521bcb09e..6d9807640f 100644 --- a/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.h +++ b/src/lib/dhcpsrv/benchmarks/generic_host_data_source_benchmark.h @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // diff --git a/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.cc b/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.cc index 19160a2b82..d30a2d951c 100644 --- a/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // diff --git a/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.h b/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.h index eb4de3926e..2532ba1e19 100644 --- a/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.h +++ b/src/lib/dhcpsrv/benchmarks/generic_lease_mgr_benchmark.h @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // diff --git a/src/lib/dhcpsrv/benchmarks/mysql_host_data_source_benchmark.cc b/src/lib/dhcpsrv/benchmarks/mysql_host_data_source_benchmark.cc index a06e9b788f..f39ed18b80 100644 --- a/src/lib/dhcpsrv/benchmarks/mysql_host_data_source_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/mysql_host_data_source_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -41,13 +42,13 @@ class MySqlHostDataSourceBenchmark : public GenericHostDataSourceBenchmark { destroyMySQLSchema(false); createMySQLSchema(false); try { - HostDataSourceFactory::destroy(); - HostDataSourceFactory::create(validMySQLConnectionString()); + HostMgr::delBackend("mysql"); + HostMgr::addBackend(validMySQLConnectionString()); } catch (...) { cerr << "ERROR: unable to open database" << endl; throw; } - hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr(); + hdsptr_ = HostMgr::instance().getHostDataSource(); } /// @brief Cleans up after the test. @@ -59,7 +60,7 @@ class MySqlHostDataSourceBenchmark : public GenericHostDataSourceBenchmark { " is opened in read-only mode, continuing..." << endl; } - HostDataSourceFactory::destroy(); + HostMgr::delBackend("mysql"); destroyMySQLSchema(false); } }; diff --git a/src/lib/dhcpsrv/benchmarks/mysql_lease_mgr_benchmark.cc b/src/lib/dhcpsrv/benchmarks/mysql_lease_mgr_benchmark.cc index c856a25ac9..4b91bb70d5 100644 --- a/src/lib/dhcpsrv/benchmarks/mysql_lease_mgr_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/mysql_lease_mgr_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // diff --git a/src/lib/dhcpsrv/benchmarks/pgsql_host_data_source_benchmark.cc b/src/lib/dhcpsrv/benchmarks/pgsql_host_data_source_benchmark.cc index 9dfd14299a..d81969c1fa 100644 --- a/src/lib/dhcpsrv/benchmarks/pgsql_host_data_source_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/pgsql_host_data_source_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -41,13 +42,13 @@ class PgSqlHostDataSourceBenchmark : public GenericHostDataSourceBenchmark { destroyPgSQLSchema(false); createPgSQLSchema(false); try { - HostDataSourceFactory::destroy(); - HostDataSourceFactory::create(validPgSQLConnectionString()); + HostMgr::delBackend("postgresql"); + HostMgr::addBackend(validPgSQLConnectionString()); } catch (...) { cerr << "ERROR: unable to open database" << endl; throw; } - hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr(); + hdsptr_ = HostMgr::instance().getHostDataSource(); } /// @brief Cleans up after the test. @@ -59,7 +60,7 @@ class PgSqlHostDataSourceBenchmark : public GenericHostDataSourceBenchmark { " is opened in read-only mode, continuing..." << endl; } - HostDataSourceFactory::destroy(); + HostMgr::delBackend("postgresql"); destroyPgSQLSchema(false); } }; diff --git a/src/lib/dhcpsrv/benchmarks/pgsql_lease_mgr_benchmark.cc b/src/lib/dhcpsrv/benchmarks/pgsql_lease_mgr_benchmark.cc index cad7acc978..637df7e123 100644 --- a/src/lib/dhcpsrv/benchmarks/pgsql_lease_mgr_benchmark.cc +++ b/src/lib/dhcpsrv/benchmarks/pgsql_lease_mgr_benchmark.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // diff --git a/src/lib/dhcpsrv/benchmarks/run_benchmarks.cc b/src/lib/dhcpsrv/benchmarks/run_benchmarks.cc index bcc4a536a9..2c1d0fa55c 100644 --- a/src/lib/dhcpsrv/benchmarks/run_benchmarks.cc +++ b/src/lib/dhcpsrv/benchmarks/run_benchmarks.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // Authors: Andrei Pavel // @@ -29,5 +29,5 @@ class Initializer { Initializer initializer; -BENCHMARK_MAIN() +BENCHMARK_MAIN(); diff --git a/src/lib/dhcpsrv/cfg_4o6.cc b/src/lib/dhcpsrv/cfg_4o6.cc index 206298124f..9a6ee8a1b7 100644 --- a/src/lib/dhcpsrv/cfg_4o6.cc +++ b/src/lib/dhcpsrv/cfg_4o6.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcpsrv/cfg_4o6.h b/src/lib/dhcpsrv/cfg_4o6.h index e231e775e6..a72357a8f1 100644 --- a/src/lib/dhcpsrv/cfg_4o6.h +++ b/src/lib/dhcpsrv/cfg_4o6.h @@ -99,7 +99,7 @@ struct Cfg4o6 : public isc::data::CfgToElement { OptionPtr interface_id_; }; -} // end of isc::dhcp namespace -} // end of isc namespace +} // namespace dhcp +} // namespace isc #endif diff --git a/src/lib/dhcpsrv/cfg_db_access.cc b/src/lib/dhcpsrv/cfg_db_access.cc index 77bc826b12..814d67a579 100644 --- a/src/lib/dhcpsrv/cfg_db_access.cc +++ b/src/lib/dhcpsrv/cfg_db_access.cc @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -41,6 +43,35 @@ CfgDbAccess::getHostDbAccessString() const { } } +std::string +CfgDbAccess::getSrvCfgMasterDbAccessString() const { + return (getAccessString(srv_cfg_master_db_access_)); +} + +std::string +CfgDbAccess::getSrvCfgDbAccessString() const { + return (getAccessString(srv_cfg_db_access_)); +} + + +void +CfgDbAccess::createSrvMasterCfgManagers() const { + // Recreate server configuration manager for the master database + SrvConfigMasterMgrFactory::destroy(); + SrvConfigMasterMgrFactory::create(getSrvCfgMasterDbAccessString()); +} + +void +CfgDbAccess::createSrvCfgManagers() const { + // Recreate server configuration manager. + SrvConfigMgrFactory::destroy(); + SrvConfigMgrFactory::create(getSrvCfgDbAccessString()); + + HostMgr::instance(); + HostMgr::delAllBackends(); + HostMgr::addBackend(getSrvCfgDbAccessString()); +} + std::list CfgDbAccess::getHostDbAccessStringList() const { std::list ret; @@ -69,7 +100,7 @@ CfgDbAccess::createManagers() const { HostMgr::checkCacheBackend(true); } -std::string +std::string CfgDbAccess::getAccessString(const std::string& access_string) const { std::ostringstream s; s << access_string; @@ -100,6 +131,10 @@ CfgDbAccess::toElementDbAccessString(const std::string& dbaccess) { std::string value = token.substr(pos + 1); if ((keyword == "lfc-interval") || (keyword == "connect-timeout") || + (keyword == "reconnect-wait-time") || + (keyword == "max-statement-tries") || + (keyword == "request-timeout") || + (keyword == "tcp-keepalive") || (keyword == "port")) { // integer parameters int64_t int_value; @@ -112,6 +147,7 @@ CfgDbAccess::toElementDbAccessString(const std::string& dbaccess) { << keyword << "=" << value); } } else if ((keyword == "persist") || + (keyword == "tcp-nodelay") || (keyword == "readonly")) { if (value == "true") { result->set(keyword, Element::create(true)); @@ -142,5 +178,5 @@ CfgDbAccess::toElementDbAccessString(const std::string& dbaccess) { return (result); } -} // end of isc::dhcp namespace -} // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cfg_db_access.h b/src/lib/dhcpsrv/cfg_db_access.h index f3f043983d..e99dfbf880 100644 --- a/src/lib/dhcpsrv/cfg_db_access.h +++ b/src/lib/dhcpsrv/cfg_db_access.h @@ -18,7 +18,7 @@ namespace isc { namespace dhcp { /// @brief Holds access parameters and the configuration of the -/// lease and hosts database connection. +/// lease, hosts and configuration database connection. /// /// The database access strings use the same format as the strings /// passed to the @ref isc::dhcp::LeaseMgrFactory::create function. @@ -68,6 +68,37 @@ class CfgDbAccess { } } + /// @brief Retrieves configuration database access string. + /// + /// @return Configuration database access string with additional parameters + /// specified with @ref CfgDbAccess::setAppendedParameters. + std::string getSrvCfgMasterDbAccessString() const; + + /// @brief Sets the master database access string. + /// + /// @param srv_cfg_master_db_access New master config database access + /// string. + void setSrvCfgMasterDbAccessString( + const std::string& srv_cfg_master_db_access) { + srv_cfg_master_db_access_ = srv_cfg_master_db_access; + } + + /// @brief Retrieves configuration database access string. + /// + /// @return Configuration database access string with additional parameters + /// specified with @ref CfgDbAccess::setAppendedParameters. + std::string getSrvCfgDbAccessString() const; + + /// @brief Sets config database access string. + /// + /// @param config_db_access New config database access string. + void setSrvCfgDbAccessString(const std::string& config_db_access) { + srv_cfg_db_access_ = config_db_access; + } + + void createSrvMasterCfgManagers() const; + void createSrvCfgManagers() const; + /// @brief Retrieves host database access string. /// /// @return Database access strings with additional parameters @@ -102,6 +133,12 @@ class CfgDbAccess { /// @brief Holds host database access strings. std::list host_db_access_; + /// @brief Holds configuration database access string. + std::string srv_cfg_db_access_; + + /// @brief Holds configuration master database access string. + std::string srv_cfg_master_db_access_; + }; /// @brief A pointer to the @c CfgDbAccess. diff --git a/src/lib/dhcpsrv/cfg_hosts.cc b/src/lib/dhcpsrv/cfg_hosts.cc index b7f52d405e..ed726c2c1d 100644 --- a/src/lib/dhcpsrv/cfg_hosts.cc +++ b/src/lib/dhcpsrv/cfg_hosts.cc @@ -21,6 +21,29 @@ using namespace isc::data; namespace isc { namespace dhcp { +HostCollection CfgHosts::getAll() const { + HostCollection collection; + + // Add hosts belonging to IPv4 subnets. + for (HostContainer::iterator it = hosts_.begin(); it != hosts_.end(); + ++it) { + collection.push_back(*it); + } + + // Add hosts belonging to IPv6 subnets. + std::set set; + for (HostContainer6::iterator it = hosts6_.begin(); it != hosts6_.end(); + ++it) { + set.insert(it->host_); + } + + for (std::set::iterator it = set.begin(); it != set.end(); ++it) { + collection.push_back(*it); + } + + return collection; +} + ConstHostCollection CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const { // Do not issue logging message here because it will be logged by @@ -104,7 +127,6 @@ CfgHosts::getAllInternal(const Host::IdentifierType& identifier_type, const uint8_t* identifier, const size_t identifier_len, Storage& storage) const { - // Convert host identifier into textual format for logging purposes. // This conversion is exception free. std::string identifier_text = Host::getIdentifierAsText(identifier_type, @@ -408,7 +430,6 @@ CfgHosts::getHostInternal6(const SubnetID& subnet_id, << subnet_id << "' and using the address '" << address.toText() << "'"); } - } template @@ -464,7 +485,8 @@ CfgHosts::getAllInternal6(const SubnetID& subnet_id, // in all sane cases, there will be only one such host. (Each host can have // multiple addresses reserved, but for each (address, subnet_id) there should // be at most one host reserving it). - for(HostContainer6Index1::iterator resrv = r.first; resrv != r.second; ++resrv) { + for (HostContainer6Index1::iterator resrv = r.first; resrv != r.second; + ++resrv) { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST) .arg(subnet_id) @@ -485,7 +507,6 @@ CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6, const Host::IdentifierType& identifier_type, const uint8_t* identifier, const size_t identifier_len) const { - LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER) .arg(subnet6 ? "IPv6" : "IPv4") .arg(subnet_id) @@ -673,7 +694,6 @@ CfgHosts::add6(const HostPtr& host) { // Now for each reservation, insert corresponding (address, host) tuple. for (IPv6ResrvIterator it = reservations.first; it != reservations.second; ++it) { - // If there's an entry for this (subnet-id, address), reject it. if (get6(host->getIPv6SubnetID(), it->second.getPrefix())) { isc_throw(DuplicateHost, "failed to add address reservation for " @@ -764,5 +784,5 @@ CfgHosts::toElement6() const { return (result.externalize()); } -} // end of namespace isc::dhcp -} // end of namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cfg_hosts.h b/src/lib/dhcpsrv/cfg_hosts.h index 0717e9a174..58f739ad2f 100644 --- a/src/lib/dhcpsrv/cfg_hosts.h +++ b/src/lib/dhcpsrv/cfg_hosts.h @@ -17,6 +17,8 @@ #include #include #include + +#include #include namespace isc { @@ -43,6 +45,13 @@ class CfgHosts : public BaseHostDataSource, public WritableHostDataSource, /// @brief Destructor. virtual ~CfgHosts() { } + /// @brief Return all hosts. + /// + /// Used when syncing reservations in kea.conf with database. + /// + /// @return Collection of const @c Host objects. + virtual HostCollection getAll() const; + /// @brief Return all hosts for the specified HW address or DUID. /// /// This method returns all @c Host objects which represent reservations @@ -389,7 +398,6 @@ class CfgHosts : public BaseHostDataSource, public WritableHostDataSource, isc::data::ElementPtr toElement() const; private: - /// @brief Returns @c Host objects for the specific identifier and type. /// /// This private method is called by the @c CfgHosts::getAllInternal diff --git a/src/lib/dhcpsrv/cfg_hosts_util.cc b/src/lib/dhcpsrv/cfg_hosts_util.cc index 5798bf51d2..d21a287815 100644 --- a/src/lib/dhcpsrv/cfg_hosts_util.cc +++ b/src/lib/dhcpsrv/cfg_hosts_util.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcpsrv/cfg_mac_source.h b/src/lib/dhcpsrv/cfg_mac_source.h index 87c673dd3d..4e1370ffba 100644 --- a/src/lib/dhcpsrv/cfg_mac_source.h +++ b/src/lib/dhcpsrv/cfg_mac_source.h @@ -80,7 +80,7 @@ class CfgMACSource : public isc::data::CfgToElement { }; -}; -}; +} +} #endif diff --git a/src/lib/dhcpsrv/cfg_srv_config_type.cc b/src/lib/dhcpsrv/cfg_srv_config_type.cc new file mode 100644 index 0000000000..6d468fd42b --- /dev/null +++ b/src/lib/dhcpsrv/cfg_srv_config_type.cc @@ -0,0 +1,26 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +namespace isc { +namespace dhcp { + +CfgSrvConfigType::CfgSrvConfigType() : type_(UNKNOWN) { +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cfg_srv_config_type.h b/src/lib/dhcpsrv/cfg_srv_config_type.h new file mode 100644 index 0000000000..90b831f075 --- /dev/null +++ b/src/lib/dhcpsrv/cfg_srv_config_type.h @@ -0,0 +1,51 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CFG_CONFIGURATIONTYPE_H +#define CFG_CONFIGURATIONTYPE_H + +namespace isc { +namespace dhcp { + +/// @brief Holds the location from where the server retrieves its configuration +/// data +/// +/// Holds the location from where the server retrieves its configuration data. +/// The configuration data may reside in a local file or in a database. +/// +class CfgSrvConfigType { +public: + /// @brief Specifies the configuration type + enum ConfigurationType { + UNKNOWN = 0, + // the configuration is read from the provided configuration file + FILE = 1, + //the configuration is read from the master database specified in the + //provided configuration file + MASTER_DATABASE = 2, + //the configuration is read from a configuration database specified in the + //provided configuration file. There is no need of master database + CONFIG_DATABASE = 3 + }; + + /// @brief Constructor. + CfgSrvConfigType(); + + ConfigurationType type_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // CFG_CONFIGURATIONTYPE_H diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 4d12e83e08..b75fbebde7 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -403,5 +403,5 @@ CfgSubnets4::toElement() const { return (result); } -} // end of namespace isc::dhcp -} // end of namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc index 2896b8bc4a..5f5f3253ee 100644 --- a/src/lib/dhcpsrv/cfgmgr.cc +++ b/src/lib/dhcpsrv/cfgmgr.cc @@ -172,10 +172,14 @@ CfgMgr::CfgMgr() // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am // Note: the definition of DHCP_DATA_DIR needs to include quotation marks // See AM_CPPFLAGS definition in Makefile.am + const char* const env = getenv("KEA_DATA_DIR"); + if (env) { + datadir_ = env; + } } CfgMgr::~CfgMgr() { } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cql_connection.cc b/src/lib/dhcpsrv/cql_connection.cc index 3b18f02a5b..58352fd3d4 100644 --- a/src/lib/dhcpsrv/cql_connection.cc +++ b/src/lib/dhcpsrv/cql_connection.cc @@ -381,7 +381,7 @@ CqlConnection::checkFutureError(const std::string& what, stream << "Session action "; } if (cass_error == CASS_OK) { - stream << " executed succesfully."; + stream << " executed successfully."; } else { stream << " failed, Kea error: " << what << ", Cassandra error code: " << cass_error_desc(cass_error) diff --git a/src/lib/dhcpsrv/cql_connection.h b/src/lib/dhcpsrv/cql_connection.h index 68ae833618..810a802a2f 100644 --- a/src/lib/dhcpsrv/cql_connection.h +++ b/src/lib/dhcpsrv/cql_connection.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2017 Deutsche Telekom AG. +// Copyright (C) 2015-2018 Deutsche Telekom AG. // // Authors: Razvan Becheriu // Andrei Pavel @@ -33,11 +33,6 @@ namespace isc { namespace dhcp { -/// @brief Pair containing major and minor versions -/// @todo: This is already defined in lease_mgr.h. Need to have one -/// definition. May need to move it if necessary. -typedef std::pair VersionPair; - /// @brief Statement index representing the statement name typedef char const* const StatementTag; diff --git a/src/lib/dhcpsrv/cql_exchange.cc b/src/lib/dhcpsrv/cql_exchange.cc index c84d7b72f4..1ff6256084 100644 --- a/src/lib/dhcpsrv/cql_exchange.cc +++ b/src/lib/dhcpsrv/cql_exchange.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Deutsche Telekom AG. +// Copyright (C) 2016-2018 Deutsche Telekom AG. // // Authors: Razvan Becheriu // Andrei Pavel @@ -986,7 +986,7 @@ CqlVersionExchange::createBindForSelect(AnyArray& data, StatementTag) { boost::any CqlVersionExchange::retrieve() { - pair_ = VersionPair(version_, minor_); + pair_ = std::make_pair(version_, minor_); return &pair_; } diff --git a/src/lib/dhcpsrv/cql_exchange.h b/src/lib/dhcpsrv/cql_exchange.h index c99f762e41..a20cd27c69 100644 --- a/src/lib/dhcpsrv/cql_exchange.h +++ b/src/lib/dhcpsrv/cql_exchange.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Deutsche Telekom AG. +// Copyright (C) 2016-2018 Deutsche Telekom AG. // // Authors: Razvan Becheriu // Andrei Pavel diff --git a/src/lib/dhcpsrv/cql_host_data_source.cc b/src/lib/dhcpsrv/cql_host_data_source.cc index 5499848024..afa1d7d091 100644 --- a/src/lib/dhcpsrv/cql_host_data_source.cc +++ b/src/lib/dhcpsrv/cql_host_data_source.cc @@ -1,5 +1,5 @@ // Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2016-2017 Deutsche Telekom AG. +// Copyright (C) 2016-2018 Deutsche Telekom AG. // // Author: Andrei Pavel // @@ -93,12 +93,6 @@ static constexpr size_t CLIENT_CLASSES_MAX_LENGTH = 255u; /// Option (see RFC4702 and RFC4704). static constexpr size_t HOSTNAME_MAX_LENGTH = 255u; -/// @brief Maximum length of option value -static constexpr size_t OPTION_VALUE_MAX_LENGTH = 4096u; - -/// @brief Maximum length of option value specified in textual format -static constexpr size_t OPTION_FORMATTED_VALUE_MAX_LENGTH = 8192u; - /// @brief Maximum length of option space name static constexpr size_t OPTION_SPACE_MAX_LENGTH = 128u; @@ -1162,12 +1156,12 @@ CqlHostExchange::retrieve() { asiolink::IOAddress ipv4_reservation = asiolink::IOAddress(static_cast(host_ipv4_address_)); - Host* host = new Host(host_identifier.data(), host_identifier.size(), + HostPtr host(new Host(host_identifier.data(), host_identifier.size(), host_identifier_type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation, hostname_, host_ipv4_client_classes_, host_ipv6_client_classes_, static_cast(host_ipv4_next_server_), - host_ipv4_server_hostname_, host_ipv4_boot_file_name_); + host_ipv4_server_hostname_, host_ipv4_boot_file_name_)); // Set the user context if there is one. if (!user_context_.empty()) { @@ -1464,6 +1458,16 @@ class CqlHostDataSourceImpl { /// @brief Implementation of @ref CqlHostDataSource::getVersion() virtual VersionPair getVersion() const; + /// @brief Implementation of @ref CqlHostDataSource::commit() + virtual void commit(); + + /// @brief Implementation of @ref CqlHostDataSource::rollback() + virtual void rollback(); + + /// @brief Implementation of @ref + /// CqlHostDataSource::syncReservations() + virtual void syncReservations(); + protected: /// @brief Adds/deletes any options found in the @ref Host object to/from a separate /// table entry. @@ -1661,6 +1665,20 @@ CqlHostDataSourceImpl::insertOrDelete(const HostPtr& host, bool insert) { return (result); } +void +CqlHostDataSourceImpl::syncReservations() { + HostCollection hosts = + CfgMgr::instance().getStagingCfg()->getCfgHosts()->getAll(); + for (HostPtr& host : hosts) { + try { + insertOrDelete(host, true); + } catch (const DuplicateEntry& exception) { + // A duplicate was being inserted. Duplicates are expected. + // Carry on. + } + } +} + ConstHostPtr CqlHostDataSourceImpl::get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { if (!address.isV4()) { @@ -1951,6 +1969,16 @@ CqlHostDataSourceImpl::getVersion() const { return (version_exchange->retrieveVersion(dbconn_)); } +void +CqlHostDataSourceImpl::commit() { + dbconn_.commit(); +} + +void +CqlHostDataSourceImpl::rollback() { + dbconn_.rollback(); +} + bool CqlHostDataSourceImpl::insertOrDeleteHostWithOptions(bool insert, const HostPtr& host, @@ -2044,7 +2072,7 @@ CqlHostDataSourceImpl::getHostCollection(StatementTag statement_tag, // Form HostPtr objects. HostCollection host_collection; for (boost::any& host : collection) { - host_collection.push_back(HostPtr(boost::any_cast(host))); + host_collection.push_back(boost::any_cast(host)); } // Merge the denormalized table entries that belong to the same host @@ -2086,7 +2114,6 @@ CqlHostDataSourceImpl::insertOrDeleteHost(bool insert, host_exchange->createBindForMutation(host, subnet_id, reservation, option_space, option_descriptor, CqlHostExchange::INSERT_HOST, assigned_values); - host_exchange->executeMutation(dbconn_, assigned_values, CqlHostExchange::INSERT_HOST); } else { host_exchange->createBindForDelete(host, subnet_id, reservation, option_space, @@ -2281,6 +2308,13 @@ CqlHostDataSource::getVersion() const { return impl_->getVersion(); } +void +CqlHostDataSource::syncReservations() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_SYNC_RESERVATIONS); + + impl_->syncReservations(); +} + void CqlHostDataSource::commit() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT); diff --git a/src/lib/dhcpsrv/cql_host_data_source.h b/src/lib/dhcpsrv/cql_host_data_source.h index 75d4d45a1d..f04f50c25b 100644 --- a/src/lib/dhcpsrv/cql_host_data_source.h +++ b/src/lib/dhcpsrv/cql_host_data_source.h @@ -357,6 +357,12 @@ class CqlHostDataSource : public BaseHostDataSource { /// Rolls back all pending database operations (no-op for Cassandra) virtual void rollback() override; + /// @brief Sync database table with kea.conf + /// + /// Truncates database tables, retrieves reservations read from kea.conf and + /// inserts coresponding hosts, reservations and options in the database. + virtual void syncReservations() override; + private: /// @brief Pointer to the implementation of the @ref CqlHostDataSource. CqlHostDataSourceImpl* impl_; diff --git a/src/lib/dhcpsrv/cql_lease_mgr.cc b/src/lib/dhcpsrv/cql_lease_mgr.cc index 0b10becfe1..5bf0f2cbf1 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.cc +++ b/src/lib/dhcpsrv/cql_lease_mgr.cc @@ -412,8 +412,7 @@ CqlLease4Exchange::createBindForInsert(const Lease4Ptr &lease, AnyArray &data) { // For convenience for external tools, this is converted to lease // expiry time (expire). The relationship is given by: // expire = cltt_ + valid_lft_ - CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, - expire_); + CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_); // subnet_id: int subnet_id_ = static_cast(lease_->subnet_id_); @@ -506,8 +505,7 @@ CqlLease4Exchange::createBindForUpdate(const Lease4Ptr &lease, AnyArray &data, // For convenience for external tools, this is converted to lease // expiry time (expire). The relationship is given by: // expire = cltt_ + valid_lft_ - CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, - expire_); + CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_); // subnet_id: int subnet_id_ = static_cast(lease_->subnet_id_); @@ -580,7 +578,7 @@ CqlLease4Exchange::createBindForSelect(AnyArray &data, StatementTag /* unused */ // Start with a fresh array. data.clear(); - // address: blob + // address: int data.add(&address_); // hwaddr: blob @@ -1131,8 +1129,7 @@ CqlLease6Exchange::createBindForUpdate(const Lease6Ptr &lease, AnyArray &data, // For convenience for external tools, this is converted to lease // expiry time (expire). The relationship is given by: // expire = cltt_ + valid_lft_ - CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, - expire_); + CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_); // subnet_id: int subnet_id_ = static_cast(lease_->subnet_id_); @@ -1459,38 +1456,38 @@ CqlLease6Exchange::getExpiredLeases(const size_t &max_leases, /// This class provides the functionality such as results storage and row /// fetching common to fulfilling the statistical lease data query. /// -class CqlLeaseStatsQuery : public LeaseStatsQuery { +class CqlLeaseStatsQuery : public LeaseStatsQuery, public CqlExchange { public: /// @brief Constructor to query for all subnets' stats /// /// The query created will return statistics for all subnets /// - /// @param conn An open connection to the database housing the lease data + /// @param connection An open connection to the database housing the lease data /// @param statement The lease data SQL prepared statement tag to execute /// @param fetch_type Indicates whether or not lease_type should be /// fetched from the result set (should be true for v6) - CqlLeaseStatsQuery(CqlConnection& conn, StatementTag& statement, - const bool fetch_type) - : conn_(conn), statement_(statement), fetch_type_(fetch_type), + CqlLeaseStatsQuery(CqlConnection& connection, StatementTag& statement, + const bool fetch_type) + : connection_(connection), statement_(statement), fetch_type_(fetch_type), cummulative_rows_(), next_row_(cummulative_rows_.begin()), - subnet_id_(0), lease_type_(0), lease_state_(0) { + subnet_id_(0), lease_type_(0), state_(0) { } /// @brief Constructor to query for a single subnet's stats /// /// The query created will return statistics for a single subnet /// - /// @param conn An open connection to the database housing the lease data + /// @param connection An open connection to the database housing the lease data /// @param statement The lease data SQL prepared statement tag to execute /// @param fetch_type Indicates whether or not lease_type should be /// fetched from the result set (should be true for v6) /// @param subnet_id id of the subnet for which stats are desired - CqlLeaseStatsQuery(CqlConnection& conn, StatementTag& statement, - const bool fetch_type, const SubnetID& subnet_id) - : LeaseStatsQuery(subnet_id), conn_(conn), statement_(statement), + CqlLeaseStatsQuery(CqlConnection& connection, StatementTag& statement, + const bool fetch_type, const SubnetID& subnet_id) + : LeaseStatsQuery(subnet_id), connection_(connection), statement_(statement), fetch_type_(fetch_type), cummulative_rows_(), next_row_(cummulative_rows_.begin()), - subnet_id_(0), lease_type_(0), lease_state_(0) { + subnet_id_(0), lease_type_(0), state_(0) { } /// @brief Constructor to query for the stats for a range of subnets @@ -1498,19 +1495,19 @@ class CqlLeaseStatsQuery : public LeaseStatsQuery { /// The query created will return statistics for the inclusive range of /// subnets described by first and last sunbet IDs. /// - /// @param conn An open connection to the database housing the lease data + /// @param connection An open connection to the database housing the lease data /// @param statement The lease data SQL prepared statement tag to execute /// @param fetch_type Indicates whether or not lease_type should be /// fetched from the result set (should be true for v6) /// @param first_subnet_id first subnet in the range of subnets /// @param last_subnet_id last subnet in the range of subnets - CqlLeaseStatsQuery(CqlConnection& conn, StatementTag& statement, - const bool fetch_type, const SubnetID& first_subnet_id, - const SubnetID& last_subnet_id) - : LeaseStatsQuery(first_subnet_id, last_subnet_id), conn_(conn), + CqlLeaseStatsQuery(CqlConnection& connection, StatementTag& statement, + const bool fetch_type, const SubnetID& first_subnet_id, + const SubnetID& last_subnet_id) + : LeaseStatsQuery(first_subnet_id, last_subnet_id), connection_(connection), statement_(statement), fetch_type_(fetch_type), cummulative_rows_(), next_row_(cummulative_rows_.begin()), - subnet_id_(0), lease_type_(0), lease_state_(0) { + subnet_id_(0), lease_type_(0), state_(0) { } /// @brief Destructor @@ -1524,35 +1521,6 @@ class CqlLeaseStatsQuery : public LeaseStatsQuery { /// first row of the aggregate results. void start(); - /// @brief Executes protocol specific lease query SELECT statement - /// - /// Currently we do not have a good way for Cassandra to roll up the - /// lease counts per subnet, type, and state as we do the other back - /// ends. This method executes the select statement which returns - /// a result set containing a row of data for every lease: - /// -v4 - subnet-id, lease-state - /// -v6 - subnet-id, lease-type, lease-state - /// - /// It then iterates over this result set, aggregating the data into a - /// a map of LeaseStatRows. - /// - /// If we didn't have to roll up the raw lease data first, we could - /// have derived this class from CqlExchange and used it's executeSelect - /// (from which this method borrows heavily). However, that would mean - /// copying all the raw lease data into a collection returned by - /// executeSelect and then aggregating that into cummulative rows. - /// The way we are now we go turn the raw lease data directly into the - /// cummulative row map. - /// - /// @param connection connection used to communicate with the Cassandra - /// database - /// @param where_values array of bound objects used to filter the results - /// @param statement_tag prepared statement being executed - /// - /// @throw DbOperationError - void executeSelect(const CqlConnection& connection, const AnyArray& data, - StatementTag statement_tag); - /// @brief Fetches the next row in the result set /// /// Once the internal result set has been populated by invoking the @@ -1576,6 +1544,15 @@ class CqlLeaseStatsQuery : public LeaseStatsQuery { virtual void createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL); + /// @brief Copy received data into the derived class' object. + /// + /// Copies information about the entity to be retrieved into a holistic + /// object. Called in @ref executeSelect(). Not implemented for base class + /// CqlExchange. To be implemented in derived classes. + /// + /// @return a pointer to the object retrieved. + virtual boost::any retrieve(); + /// @brief Statement tags definitions /// @{ // Return lease4 lease statistics for all subnets @@ -1584,7 +1561,6 @@ class CqlLeaseStatsQuery : public LeaseStatsQuery { static constexpr StatementTag SUBNET_LEASE4_STATS = "SUBNET_LEASE4_STATS"; /// Return lease4 lease statistics for a range of subnets static constexpr StatementTag SUBNET_RANGE_LEASE4_STATS = "SUBNET_RANGE_LEASE4_STATS"; - // Return lease6 lease statistics for all subnets static constexpr StatementTag ALL_LEASE6_STATS = "ALL_LEASE6_STATS"; /// Return lease6 lease statistics for a single subnet @@ -1597,28 +1573,29 @@ class CqlLeaseStatsQuery : public LeaseStatsQuery { static StatementMap tagged_statements_; private: - /// @brief Database connection to use to execute the query - CqlConnection& conn_; + /// @brief Database connection + const CqlConnection &connection_; /// @brief The query's prepared statement tag StatementTag statement_; - /// @brief Indicates if query supplies lease type + /// @brief fetch from the result set? (should be true for v6) bool fetch_type_; - /// @brief map containing the aggregated lease counts std::map cummulative_rows_; /// @brief cursor pointing to the next row to read in aggregate map std::map::iterator next_row_; - /// @brief bind variable for retrieving subnet-id from a result set row - int subnet_id_; - /// @brief bind variable for retrieving lease-type from a result set row - int lease_type_; - /// @brief bind variable for retrieving lease-state from a result set row - int lease_state_; + /// @brief Subnet identifier + cass_int32_t subnet_id_; + + /// @brief Lease type (NA, TA or PD) + cass_int32_t lease_type_; + + /// @brief Lease state + cass_int32_t state_; }; constexpr StatementTag CqlLeaseStatsQuery::ALL_LEASE4_STATS; @@ -1682,12 +1659,10 @@ StatementMap CqlLeaseStatsQuery::tagged_statements_{ "WHERE subnet_id >= ? and subnet_id <= ? " "ALLOW FILTERING " }}, - }; void CqlLeaseStatsQuery::start() { - // Set up where clause parameters as needed AnyArray data; cass_int32_t first_subnet_id_data; @@ -1701,11 +1676,24 @@ CqlLeaseStatsQuery::start() { data.add(&last_subnet_id_data); } } - - // This gets a collection of "raw" data for all leases that match - // the subnet selection criteria (all, range, or single subnets) - // then rolls them up into cummulative_rows_ - executeSelect(conn_, data, statement_); + AnyArray collection = executeSelect(connection_, data, statement_); + + // Form LeaseStatsRowPtr objects. + LeaseStatsCollection stats_collection; + for (boost::any& stats : collection) { + LeaseStatsRowPtr data(boost::any_cast(stats)); + if (data->lease_state_ != Lease::STATE_DEFAULT && + data->lease_state_ != Lease::STATE_DECLINED) { + continue; + } + stats_collection.push_back(data); + auto cum_row = cummulative_rows_.find(*data); + if (cum_row != cummulative_rows_.end()) { + cummulative_rows_[*data] = cum_row->second + 1; + } else { + cummulative_rows_.insert(std::make_pair(*data, 1)); + } + } // Set our row iterator to the beginning next_row_ = cummulative_rows_.begin(); @@ -1732,123 +1720,29 @@ CqlLeaseStatsQuery::getNextRow(LeaseStatsRow& row) { } void -CqlLeaseStatsQuery::createBindForSelect(AnyArray& data, StatementTag) { +CqlLeaseStatsQuery::createBindForSelect(AnyArray& data, StatementTag /* statement_tag */) { + + // Start with a fresh array. data.clear(); + + // subnet_id: int data.add(&subnet_id_); + + // lease_type: int if (fetch_type_) { data.add(&lease_type_); - } - - data.add(&lease_state_); -} - -void -CqlLeaseStatsQuery::executeSelect(const CqlConnection& connection, const AnyArray& data, - StatementTag statement_tag) { - CassError rc; - CassStatement* statement = NULL; - CassFuture* future = NULL; - AnyArray local_data = data; - - // Find the query statement first. - StatementMap::const_iterator it = connection.statements_.find(statement_tag); - if (it == connection.statements_.end()) { - isc_throw(DbOperationError, - "CqlLeastStatsQuery::executeSelect(): Statement " - << statement_tag << "has not been prepared."); - } - - // Bind the data before the query is executed. - CqlTaggedStatement tagged_statement = it->second; - if (tagged_statement.is_raw_) { - // The entire query is the first element in data. - std::string* query = boost::any_cast(local_data.back()); - local_data.pop_back(); - statement = cass_statement_new(query->c_str(), local_data.size()); } else { - statement = cass_prepared_bind(tagged_statement.prepared_statement_); - if (!statement) { - isc_throw(DbOperationError, - "CqlLeaseStatsQuery::executeSelect(): unable to bind statement " - << tagged_statement.name_); - } - } - - // Set specific level of consistency if we're told to do so. - if (connection.force_consistency_) { - rc = cass_statement_set_consistency(statement, connection.consistency_); - if (rc != CASS_OK) { - cass_statement_free(statement); - isc_throw(DbOperationError, - "CqlLeaseStatsQuery::executeSelect(): unable to set statement " - "consistency for statement " - << tagged_statement.name_ - << ", Cassandra error code: " << cass_error_desc(rc)); - } - } - - CqlCommon::bindData(local_data, statement); - - // Everything's ready. Call the actual statement. - future = cass_session_execute(connection.session_, statement); - if (!future) { - cass_statement_free(statement); - isc_throw(DbOperationError, - "CqlLeaseStatsQuery::executeSelect(): no CassFuture for statement " - << tagged_statement.name_); - } - - // Wait for the statement execution to complete. - cass_future_wait(future); - const std::string error = connection.checkFutureError( - "CqlLeaseStatsQuery::executeSelect(): cass_session_execute() != CASS_OK", - future, statement_tag); - rc = cass_future_error_code(future); - if (rc != CASS_OK) { - cass_future_free(future); - cass_statement_free(statement); - isc_throw(DbOperationError, error); + lease_type_ = Lease::TYPE_NA; // lease type is always NA for v4 } - // Get column values. - const CassResult* result_collection = cass_future_get_result(future); - - // lease type is always NA for v4 - if (!fetch_type_) { - lease_type_ = Lease::TYPE_NA; - } - - // Since we're currently forced to pull data for all leases, we - // iterate over them, aggregating them into cummulative LeaseStatsRows - AnyArray return_values; - CassIterator* rows = cass_iterator_from_result(result_collection); - while (cass_iterator_next(rows)) { - const CassRow* row = cass_iterator_get_row(rows); - createBindForSelect(return_values, statement_tag); - CqlCommon::getData(row, return_values); - - if (lease_state_ != Lease::STATE_DEFAULT && - lease_state_ != Lease::STATE_DECLINED) { - continue; - } - - LeaseStatsRow raw_row(subnet_id_, static_cast(lease_type_), - lease_state_, 1); - - auto cum_row = cummulative_rows_.find(raw_row); - if (cum_row != cummulative_rows_.end()) { - cummulative_rows_[raw_row] = cum_row->second + 1; - } else { - cummulative_rows_.insert(std::make_pair(raw_row, 1)); - } - } + // state: int + data.add(&state_); +} - // Free resources. - cass_iterator_free(rows); - cass_result_free(result_collection); - cass_future_free(future); - cass_statement_free(statement); - return; +boost::any +CqlLeaseStatsQuery::retrieve() { + return (LeaseStatsRowPtr(new LeaseStatsRow(subnet_id_, + static_cast(lease_type_), state_, 1))); } CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap ¶meters) @@ -1870,7 +1764,9 @@ CqlLeaseMgr::getDBVersion() { std::stringstream tmp; tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR; tmp << "." << CQL_SCHEMA_VERSION_MINOR; - tmp << ", library cassandra"; + tmp << ", library cassandra_static " << CASS_VERSION_MAJOR; + tmp << "." << CASS_VERSION_MINOR; + tmp << "." << CASS_VERSION_PATCH; return tmp.str(); } @@ -2284,7 +2180,7 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED6) .arg(secs); AnyArray data; - uint64_t n_of_deleted_leases = 0u; + uint64_t deleted = 0u; cass_int32_t limit = 1024; // State is reclaimed. @@ -2303,10 +2199,10 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_EXPIRE, data, leases); for (Lease6Ptr &lease : leases) { if (deleteLease(lease->addr_)) { - ++n_of_deleted_leases; + ++deleted; } } - return n_of_deleted_leases; + return (deleted); } LeaseStatsQueryPtr @@ -2329,7 +2225,7 @@ CqlLeaseMgr::startSubnetLeaseStatsQuery4(const SubnetID& subnet_id) { LeaseStatsQueryPtr CqlLeaseMgr::startSubnetRangeLeaseStatsQuery4(const SubnetID& first_subnet_id, - const SubnetID& last_subnet_id) { + const SubnetID& last_subnet_id) { LeaseStatsQueryPtr query( new CqlLeaseStatsQuery(dbconn_, CqlLeaseStatsQuery::SUBNET_RANGE_LEASE4_STATS, false, first_subnet_id, last_subnet_id)); @@ -2357,7 +2253,7 @@ CqlLeaseMgr::startSubnetLeaseStatsQuery6(const SubnetID& subnet_id) { LeaseStatsQueryPtr CqlLeaseMgr::startSubnetRangeLeaseStatsQuery6(const SubnetID& first_subnet_id, - const SubnetID& last_subnet_id) { + const SubnetID& last_subnet_id) { LeaseStatsQueryPtr query( new CqlLeaseStatsQuery(dbconn_, CqlLeaseStatsQuery::SUBNET_RANGE_LEASE6_STATS, true, first_subnet_id, last_subnet_id)); @@ -2401,6 +2297,12 @@ CqlLeaseMgr::getVersion() const { return version_exchange->retrieveVersion(dbconn_); } +bool +CqlLeaseMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_BEGIN_TRANSACTION); + dbconn_.startTransaction(); + return true; +} void CqlLeaseMgr::commit() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT); diff --git a/src/lib/dhcpsrv/cql_lease_mgr.h b/src/lib/dhcpsrv/cql_lease_mgr.h index ecd47864a7..3c0cc1ba75 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.h +++ b/src/lib/dhcpsrv/cql_lease_mgr.h @@ -105,7 +105,7 @@ class CqlLeaseMgr : public LeaseMgr { /// @brief Returns an IPv4 lease for specified IPv4 address /// - /// This method return a lease that is associated with a given address. + /// This method returns a lease that is associated with a given address. /// For other query types (by hardware addr, by Client ID) there can be /// several leases in different subnets (e.g. for mobile clients that /// got address in different subnets). However, for a single address @@ -484,6 +484,12 @@ class CqlLeaseMgr : public LeaseMgr { /// failed. virtual VersionPair getVersion() const override; + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() override; + /// @brief Commit Transactions /// /// This is a no-op for Cassandra. diff --git a/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc new file mode 100644 index 0000000000..27731b1da0 --- /dev/null +++ b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc @@ -0,0 +1,922 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Authors: Cristian Secareanu +// Andrei Pavel +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include +#include + +#include + +#include // for int32_t + +namespace isc { +namespace dhcp { + +StatementMap CqlMasterConfigVersionExchange::tagged_statements_ = { + {GET_VERSION, // + {GET_VERSION, // + "SELECT " + "version, minor " + "FROM master_schema_version "}} // +}; + +/// @brief Exchange used to interact with the master server configuration table +/// from the database +class CqlMasterConfigExchange : public virtual CqlExchange { +public: + /// @brief Statement tags definitions + /// @{ + // Insert DHCPv4 server configuration. + static constexpr StatementTag INSERT_SERVER_CONFIG4 = "INSERT_SERVER_CONFIG4"; + // Insert DHCPv6 server configuration. + static constexpr StatementTag INSERT_SERVER_CONFIG6 = "INSERT_SERVER_CONFIG6"; + // Retrieve DHCPv4 server configuration filtered by server ID. + static constexpr StatementTag GET_CONFIGURATION4_BY_SRV_ID = "GET_CONFIGURATION4_BY_SRV_ID"; + // Retrieve DHCPv6 server configuration filtered by server ID. + static constexpr StatementTag GET_CONFIGURATION6_BY_SRV_ID = "GET_CONFIGURATION6_BY_SRV_ID"; + // Retrieve DHCPv4 server configuration filtered by shard name. + static constexpr StatementTag GET_CONFIGURATION4_BY_SHARD_DB = "GET_CONFIGURATION4_BY_SHARD_DB"; + // Retrieve DHCPv6 server configuration filtered by shard name. + static constexpr StatementTag GET_CONFIGURATION6_BY_SHARD_DB = "GET_CONFIGURATION6_BY_SHARD_DB"; + // Retrieve DHCPv4 synchronization timestamp. + static constexpr StatementTag GET_CONFIGURATION4_TIMESTAMP = "GET_CONFIGURATION4_TIMESTAMP"; + // Retrieve DHCPv6 synchronization timestamp. + static constexpr StatementTag GET_CONFIGURATION6_TIMESTAMP = "GET_CONFIGURATION6_TIMESTAMP"; + // Retrieve DHCPv4 server configurations. + static constexpr StatementTag GET_SERVERS_CONFIG4 = "GET_SERVERS_CONFIG4"; + // Retrieve DHCPv6 server configurations. + static constexpr StatementTag GET_SERVERS_CONFIG6 = "GET_SERVERS_CONFIG6"; + // Delete DHCPv4 server configuration. + static constexpr StatementTag DELETE_SERVER_CONFIG4 = "DELETE_SERVER_CONFIG4"; + // Delete DHCPv6 server configuration. + static constexpr StatementTag DELETE_SERVER_CONFIG6 = "DELETE_SERVER_CONFIG6"; + // Retrieve DHCPv4 shard names. + static constexpr StatementTag GET_SERVERS_CONFIG4_SHARDS_NAME = + "GET_SERVERS_CONFIG4_SHARDS_NAME"; + // Retrieve DHCPv6 shard names. + static constexpr StatementTag GET_SERVERS_CONFIG6_SHARDS_NAME = + "GET_SERVERS_CONFIG6_SHARDS_NAME"; + /// @} + + /// @brief Constructor + /// + /// Specifies table columns. + CqlMasterConfigExchange(); + + /// @brief Destructor + virtual ~CqlMasterConfigExchange(); + + /// @brief Create BIND array to receive master configuration data. + /// + /// Method used in executeSelect() to retrieve SrvConfigMasterInfoPtr object + /// from database + /// + /// @param data array of bound objects representing data to be retrieved. + /// @param statement_tag prepared statement being executed; defaults to an + /// invalid index + virtual void createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL) override; + + /// @brief Copy received data into @ref SrvConfigMasterInfo object + /// + /// This method copies information about the master server configuration + /// into a newly created @ref SrvConfigMasterInfo object. This method is + /// called in @ref executeSelect(). + /// + /// @return pointer to a @ref SrvConfigMasterInfoPtr object holding a + /// pointer to the @ref SrvConfigMasterInfo object returned. + virtual boost::any retrieve() override; + + /// @brief Common method for inserting configuration information into the + /// database. + /// + /// @param connection connection used to communicate with the Cassandra + /// database + /// @param instance_id table column - unique instance identifier + /// @param server_config table column - JSON server configuration + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// @param statement_tag prepared statement being executed + /// + /// @return true if insertion succeeded, false otherwise + bool insertCommon(CqlConnection& connection, + const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name, + StatementTag statement_tag); + + /// @brief Common method for retrieving configuration information from the + /// database. + SrvConfigMasterInfoCollection getCommon(CqlConnection& connection, + const AnyArray& data, + StatementTag statement_tag, + bool single = true); + + /// @brief Cassandra statements + static StatementMap tagged_statements_; + +private: + /// @brief Object that this exchange reads from when writing to the database + /// and writes to when reading from the database. + SrvConfigMasterInfoPtr master_config_info_; +}; // CqlMasterConfigExchange + +constexpr StatementTag CqlMasterConfigExchange::INSERT_SERVER_CONFIG4; +constexpr StatementTag CqlMasterConfigExchange::INSERT_SERVER_CONFIG6; +constexpr StatementTag CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SRV_ID; +constexpr StatementTag CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SRV_ID; +constexpr StatementTag CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SHARD_DB; +constexpr StatementTag CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SHARD_DB; +constexpr StatementTag CqlMasterConfigExchange::GET_CONFIGURATION4_TIMESTAMP; +constexpr StatementTag CqlMasterConfigExchange::GET_CONFIGURATION6_TIMESTAMP; +constexpr StatementTag CqlMasterConfigExchange::GET_SERVERS_CONFIG4; +constexpr StatementTag CqlMasterConfigExchange::GET_SERVERS_CONFIG6; +constexpr StatementTag CqlMasterConfigExchange::DELETE_SERVER_CONFIG4; +constexpr StatementTag CqlMasterConfigExchange::DELETE_SERVER_CONFIG6; +constexpr StatementTag CqlMasterConfigExchange::GET_SERVERS_CONFIG4_SHARDS_NAME; +constexpr StatementTag CqlMasterConfigExchange::GET_SERVERS_CONFIG6_SHARDS_NAME; + +StatementMap CqlMasterConfigExchange::tagged_statements_ = { + {INSERT_SERVER_CONFIG4, // + {INSERT_SERVER_CONFIG4, // + "INSERT INTO server_configuration4 " + "(instance_id, timestamp, server_config, config_database, config_database_name) " + "VALUES (?, ?, ?, ?, ?) " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#else // TERASTREAM_FULL_TRANSACTIONS + "IF NOT EXISTS " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {INSERT_SERVER_CONFIG6, // + {INSERT_SERVER_CONFIG6, // + "INSERT INTO server_configuration6 " + "(instance_id, timestamp, server_config, config_database, config_database_name) " + "VALUES (?, ?, ?, ?, ?) " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#else // TERASTREAM_FULL_TRANSACTIONS + "IF NOT EXISTS " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_CONFIGURATION4_BY_SRV_ID, // + {GET_CONFIGURATION4_BY_SRV_ID, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id, timestamp, server_config, config_database, config_database_name " + "FROM server_configuration4 " + "WHERE instance_id = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_CONFIGURATION6_BY_SRV_ID, // + {GET_CONFIGURATION6_BY_SRV_ID, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id, timestamp, server_config, config_database, config_database_name " + "FROM server_configuration6 " + "WHERE instance_id = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_CONFIGURATION4_BY_SHARD_DB, // + {GET_CONFIGURATION4_BY_SHARD_DB, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id, timestamp, server_config, config_database, config_database_name " + "FROM server_configuration4 " + "WHERE config_database_name = ? ALLOW FILTERING " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_CONFIGURATION6_BY_SHARD_DB, // + {GET_CONFIGURATION6_BY_SHARD_DB, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id, timestamp, server_config, config_database, config_database_name " + "FROM server_configuration6 " + "WHERE config_database_name = ? ALLOW FILTERING " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_CONFIGURATION4_TIMESTAMP, // + {GET_CONFIGURATION4_TIMESTAMP, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id, timestamp " + "FROM server_configuration4 " + "WHERE instance_id = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_CONFIGURATION6_TIMESTAMP, // + {GET_CONFIGURATION6_TIMESTAMP, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id, timestamp " + "FROM server_configuration6 " + "WHERE instance_id = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_SERVERS_CONFIG4, // + {GET_SERVERS_CONFIG4, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id " + "FROM server_configuration4 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_SERVERS_CONFIG6, // + {GET_SERVERS_CONFIG6, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT instance_id " + "FROM server_configuration6 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {DELETE_SERVER_CONFIG4, // + {DELETE_SERVER_CONFIG4, // + "DELETE FROM server_configuration4 " + "WHERE instance_id = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#else // TERASTREAM_FULL_TRANSACTIONS + "IF EXISTS " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {DELETE_SERVER_CONFIG6, // + {DELETE_SERVER_CONFIG6, // + "DELETE FROM server_configuration6 " + "WHERE instance_id = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#else // TERASTREAM_FULL_TRANSACTIONS + "IF EXISTS " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_SERVERS_CONFIG4_SHARDS_NAME, // + {GET_SERVERS_CONFIG4_SHARDS_NAME, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_database_name " + "FROM server_configuration4 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + {GET_SERVERS_CONFIG6_SHARDS_NAME, // + {GET_SERVERS_CONFIG6_SHARDS_NAME, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_database_name " + "FROM server_configuration6 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }} // +}; + +CqlMasterConfigExchange::CqlMasterConfigExchange() + : master_config_info_(boost::make_shared()) { +} + +CqlMasterConfigExchange::~CqlMasterConfigExchange() { +} + +void CqlMasterConfigExchange::createBindForSelect(AnyArray& data, + StatementTag statement_tag /* = NULL */) { + // Start with a fresh array. + data.clear(); + + if (statement_tag == GET_CONFIGURATION4_TIMESTAMP || + statement_tag == GET_CONFIGURATION6_TIMESTAMP) { + data.add(&master_config_info_->instance_id_); + data.add(&master_config_info_->timestamp_); + } else if (statement_tag == GET_SERVERS_CONFIG4_SHARDS_NAME || + statement_tag == GET_SERVERS_CONFIG6_SHARDS_NAME) { + data.add(&master_config_info_->config_database_name_); + } else if (statement_tag == GET_SERVERS_CONFIG4 || statement_tag == GET_SERVERS_CONFIG6) { + data.add(&master_config_info_->instance_id_); + } else { + data.add(&master_config_info_->instance_id_); + data.add(&master_config_info_->timestamp_); + data.add(&master_config_info_->server_config_); + data.add(&master_config_info_->config_database_); + data.add(&master_config_info_->config_database_name_); + } +} + +boost::any CqlMasterConfigExchange::retrieve() { + SrvConfigMasterInfoPtr config(new SrvConfigMasterInfo(*master_config_info_)); + return config; +} + +SrvConfigMasterInfoCollection CqlMasterConfigExchange::getCommon(CqlConnection& connection, + const AnyArray& data, + StatementTag statement_tag, + bool single /* = true */) { + AnyArray collection = executeSelect(connection, data, statement_tag, single); + + SrvConfigMasterInfoCollection result; + + if (collection.empty()) { + return result; + } + + for (boost::any& element : collection) { + SrvConfigMasterInfoPtr item(boost::any_cast(element)); + result.push_back(item); + } + + return result; +} + +bool CqlMasterConfigExchange::insertCommon(CqlConnection& connection, + const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name, + StatementTag statement_tag) { + cass_int64_t config_timestamp = static_cast(time(NULL)); + + AnyArray assigned_values; + assigned_values.add(const_cast(&instance_id)); + assigned_values.add(&config_timestamp); + assigned_values.add(const_cast(&server_config)); + assigned_values.add(const_cast(&config_database)); + assigned_values.add(const_cast(&config_database_name)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = connection.getTransactionId(); + assigned_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + executeMutation(connection, assigned_values, statement_tag); + + return true; +} + +CqlSrvConfigMasterMgr::CqlSrvConfigMasterMgr(const DatabaseConnection::ParameterMap& parameters) + : SrvConfigMasterMgr(), dbconn_(parameters) { + dbconn_.openDatabase(); +#ifdef TERASTREAM_FULL_TRANSACTIONS + dbconn_.setTransactionOperations(CqlTransactionExchange::BEGIN_TXN, + CqlTransactionExchange::COMMIT_TXN, + CqlTransactionExchange::ROLLBACK_TXN); +#endif // TERASTREAM_FULL_TRANSACTIONS + dbconn_.prepareStatements(CqlMasterConfigExchange::tagged_statements_); + dbconn_.prepareStatements(CqlMasterConfigVersionExchange::tagged_statements_); +#ifdef TERASTREAM_FULL_TRANSACTIONS + dbconn_.prepareStatements(CqlTransactionExchange::tagged_statements_); +#endif // TERASTREAM_FULL_TRANSACTIONS +} + +CqlSrvConfigMasterMgr::~CqlSrvConfigMasterMgr() { +} + +bool CqlSrvConfigMasterMgr::addServerConfig4(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG4) + .arg(instance_id) + .arg(config_database_name); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + master_config_exchange->insertCommon(dbconn_, instance_id, server_config, config_database, + config_database_name, + CqlMasterConfigExchange::INSERT_SERVER_CONFIG4); + + transaction.commit(); + + return true; +} + +bool CqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG6) + .arg(instance_id) + .arg(config_database_name); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + master_config_exchange->insertCommon(dbconn_, instance_id, server_config, config_database, + config_database_name, + CqlMasterConfigExchange::INSERT_SERVER_CONFIG6); + + transaction.commit(); + + return true; +} + +SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig4(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4) + .arg(instance_id); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray where_values; + where_values.add(const_cast(&instance_id)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + where_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( + dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SRV_ID); + + transaction.commit(); + + SrvConfigMasterInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig6(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6) + .arg(instance_id); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray where_values; + where_values.add(const_cast(&instance_id)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + where_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( + dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SRV_ID); + + transaction.commit(); + + SrvConfigMasterInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +bool CqlSrvConfigMasterMgr::getConfig4( + const std::string& config_database_name, + std::vector& server_configurations) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARD_DB) + .arg(config_database_name); + + server_configurations.clear(); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray where_values; + where_values.add(const_cast(&config_database_name)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + where_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( + dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SHARD_DB, false); + + transaction.commit(); + + server_configurations = collection_data; + + return true; +} + +bool CqlSrvConfigMasterMgr::getConfig6( + const std::string& config_database_name, + std::vector& server_configurations) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARD_DB) + .arg(config_database_name); + + server_configurations.clear(); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray where_values; + where_values.add(const_cast(&config_database_name)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + where_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( + dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SHARD_DB, false); + + transaction.commit(); + + server_configurations = collection_data; + + return true; +} + +SrvConfigMasterInfoPtr +CqlSrvConfigMasterMgr::getMasterConfig4Timestamp(const std::string& instance_id) const { + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP) + .arg(instance_id); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray where_values; + + where_values.add(const_cast(&instance_id)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + where_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( + dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_TIMESTAMP); + + transaction.commit(); + + SrvConfigMasterInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +SrvConfigMasterInfoPtr +CqlSrvConfigMasterMgr::getMasterConfig6Timestamp(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP) + .arg(instance_id); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray where_values; + + where_values.add(const_cast(&instance_id)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + where_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( + dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_TIMESTAMP); + + transaction.commit(); + + SrvConfigMasterInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +bool CqlSrvConfigMasterMgr::clearServersConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG4); + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::vector servers_id; + getServersConfig4(servers_id); + for (std::string const& server_id : servers_id) { + deleteServerConfig4(server_id); + } + + transaction.commit(); + + return true; +} + +bool CqlSrvConfigMasterMgr::clearServersConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG6); + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::vector servers_id; + getServersConfig6(servers_id); + for (std::string const& server_id : servers_id) { + deleteServerConfig6(server_id); + } + + transaction.commit(); + + return true; +} + +bool CqlSrvConfigMasterMgr::getServersConfig4ShardsName(std::set& shards_list) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME); + + shards_list.clear(); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray data; + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + data.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( + dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG4_SHARDS_NAME, false); + + transaction.commit(); + + for (SrvConfigMasterInfoPtr const& srvInfo : collection_data) { + shards_list.insert(srvInfo->config_database_name_); + } + + return true; +} + +bool CqlSrvConfigMasterMgr::getServersConfig6ShardsName(std::set& shards_list) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME); + + shards_list.clear(); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray data; + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + data.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( + dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG6_SHARDS_NAME, false); + + transaction.commit(); + + for (SrvConfigMasterInfoPtr const& srvInfo : collection_data) { + shards_list.insert(srvInfo->config_database_name_); + } + + return true; +} + +std::string CqlSrvConfigMasterMgr::getDBVersion() { + std::stringstream tmp; + tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR; + tmp << "." << CQL_SCHEMA_VERSION_MINOR; + tmp << ", library cassandra_static " << CASS_VERSION_MAJOR; + tmp << "." << CASS_VERSION_MINOR; + tmp << "." << CASS_VERSION_PATCH; + return tmp.str(); +} + +std::string CqlSrvConfigMasterMgr::getType() const { + return (std::string("cql")); +} + +VersionPair CqlSrvConfigMasterMgr::getVersion() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_VERSION); + + std::unique_ptr version_exchange(new CqlVersionExchange()); + return version_exchange->retrieveVersion(dbconn_); +} + +bool CqlSrvConfigMasterMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_BEGIN_TRANSACTION); + dbconn_.startTransaction(); + return true; +} + +void CqlSrvConfigMasterMgr::commit() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT); + dbconn_.commit(); +} + +void CqlSrvConfigMasterMgr::rollback() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK); + dbconn_.rollback(); +} + +///////////////////////////////////////////////////////// +// Protected methods + +bool CqlSrvConfigMasterMgr::getServersConfig4(std::vector& servers_id) const { + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + + servers_id.clear(); + CqlTransaction transaction(dbconn_); + + AnyArray data; + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + data.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + AnyArray collection = master_config_exchange->executeSelect( + dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG4); + + transaction.commit(); + + if (collection.empty()) { + return true; + } + + for (boost::any& element : collection) { + SrvConfigMasterInfoPtr item(boost::any_cast(element)); + servers_id.push_back(item->instance_id_); + } + + return true; +} + +bool CqlSrvConfigMasterMgr::getServersConfig6(std::vector& servers_id) const { + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + + servers_id.clear(); + CqlTransaction transaction(dbconn_); + + AnyArray data; + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + data.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + AnyArray collection = master_config_exchange->executeSelect( + dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG6); + + transaction.commit(); + + if (collection.empty()) { + return true; + } + + for (boost::any& element : collection) { + SrvConfigMasterInfoPtr item(boost::any_cast(element)); + servers_id.push_back(item->instance_id_); + } + + return true; +} + +bool CqlSrvConfigMasterMgr::deleteServerConfig4(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG4) + .arg(instance_id); + + // CqlTransaction rolls back the transaction on its destructor. + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray data; + data.add(const_cast(&instance_id)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + data.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + master_config_exchange->executeMutation(dbconn_, data, + CqlMasterConfigExchange::DELETE_SERVER_CONFIG4); + + transaction.commit(); + + return true; +} + +bool CqlSrvConfigMasterMgr::deleteServerConfig6(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG6) + .arg(instance_id); + + // CqlTransaction rolls back the transaction on its destructor. + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + AnyArray data; + data.add(const_cast(&instance_id)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = dbconn_.getTransactionId(); + data.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); + master_config_exchange->executeMutation(dbconn_, data, + CqlMasterConfigExchange::DELETE_SERVER_CONFIG6); + + transaction.commit(); + + return true; +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cql_srv_config_master_mgr.h b/src/lib/dhcpsrv/cql_srv_config_master_mgr.h new file mode 100644 index 0000000000..799508b2c6 --- /dev/null +++ b/src/lib/dhcpsrv/cql_srv_config_master_mgr.h @@ -0,0 +1,244 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Authors: Cristian Secareanu +// Andrei Pavel +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CQL_CONFIGURATION_MASTER_MGR_H +#define CQL_CONFIGURATION_MASTER_MGR_H + +#include + +#include +#include + +#include // for std::scoped_ptr + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Forward declaration to the exchange. +class CqlMasterConfigExchange; + +/// @brief Exchange used to interact with the master server configuration table +/// from the database +class CqlSrvConfigMasterMgr : public SrvConfigMasterMgr { +public: + /// @brief Constructor + /// + /// @param parameters database connection parameters + explicit CqlSrvConfigMasterMgr(const DatabaseConnection::ParameterMap& parameters); + + /// @brief Destructor + virtual ~CqlSrvConfigMasterMgr(); + + /// @brief Inserts DHCPv4 server configuration into the master database + /// + /// @param instance_id instance identifier of the server whose configuration is being added + /// @param server_config the configuration being added + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true if server configuration has been added, false otherwise + virtual bool addServerConfig4(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const override; + + /// @brief Inserts DHCPv6 server configuration into the master database + /// + /// @param instance_id instance identifier of the server whose configuration is being added + /// @param server_config the configuration being added + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true if server configuration has been added, false otherwise + virtual bool addServerConfig6(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const override; + + /// @brief Clears all DHCPv4 shard configurations. + /// + /// @return true if shard configurations have been successfully deleted, false otherwise + virtual bool clearServersConfig4() const override; + + /// @brief Clears all DHCPv6 shard configurations. + /// + /// @return true if shard configurations have been successfully deleted, false otherwise + virtual bool clearServersConfig6() const override; + + /// @brief Returns DHCPv4 server configuration information from the database + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one Kea server. + /// + /// @param instance_id the instance identifier of the server being retrieved + /// + /// @return smart pointer to the configuration (or NULL if a configuration is not found) + virtual SrvConfigMasterInfoPtr getConfig4(const std::string& instance_id) const override; + + /// @brief Returns DHCPv6 server configuration information from the database + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one Kea server. + /// + /// @param instance_id the instance identifier of the server being retrieved + /// + /// @return smart pointer to the configuration (or NULL if a configuration is not found) + virtual SrvConfigMasterInfoPtr getConfig6(const std::string& instance_id) const override; + + /// @brief Retrieves configurations of DHCPv4 servers belonging to a given + /// shard. + /// + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations a list of server configurations being retrieved + /// + /// @return true if server configuration has been successfully retrieved, false otherwise + virtual bool getConfig4(const std::string& config_database_name, + std::vector& server_configurations) const override; + + /// @brief Retrieves configurations of DHCPv6 servers belonging to a given + /// shard. + /// + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations a list of server configurations being retrieved + /// + /// @return true if server configuration has been successfully retrieved, false otherwise + virtual bool getConfig6(const std::string& config_database_name, + std::vector& server_configurations) const override; + + /// @brief Retrieves timestamp for a DHCPv4 shard server configuration. + /// + /// There is one timestamp for each shard in the master database. They are + /// updated on every write (including updates) to the master database. + /// + /// @param instance_id the instance identifier of the server for which the timestamp is being + /// retrieved + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigMasterInfoPtr + getMasterConfig4Timestamp(const std::string& instance_id) const override; + + /// @brief Retrieves timestamp for a DHCPv6 shard server configuration. + /// + /// There is one timestamp for each shard in the master database. They are + /// updated on every write (including updates) to the master database. + /// + /// @param instance_id the instance identifier of the server for which the timestamp is being + /// retrieved + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigMasterInfoPtr + getMasterConfig6Timestamp(const std::string& instance_id) const override; + + /// @brief Retrieves all shard names containing DHCPv4 servers from the master database. + /// + /// @return true if shard names have been successfully retrieved, false otherwise + virtual bool getServersConfig4ShardsName(std::set& shards) const override; + + /// @brief Retrieves all shard names containing DHCPv6 servers from the master database. + /// + /// @return true if shard names have been successfully retrieved, false otherwise + virtual bool getServersConfig6ShardsName(std::set& shards) const override; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() override; + + /// @brief Commit Transactions + /// + /// This is a no-op for Cassandra. + virtual void commit() override; + + /// @brief Rollback Transactions + /// + /// This is a no-op for Cassandra. + virtual void rollback() override; + + /// @brief Retrieves schema version. + /// + /// @return Version number stored in the database, as a pair of unsigned integers. "first" is + /// the major version number, "second" is the minor version number. + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has failed. + VersionPair getVersion() const override; + + /// @brief Local version of getDBVersion() class method + static std::string getDBVersion(); + + /// @brief Returns the type of backend used, in this case "cql". Useful + /// when called from the base class. + virtual std::string getType() const override; + +protected: + /// @brief Generic method for retrieving DHCPv4 shard configurations. + /// + /// @param servers_id list of shard configurations being populated + /// + /// @return true if shard configurations have been successfully retrieved, + /// false otherwise + bool getServersConfig4(std::vector& servers_id) const; + + /// @brief Generic method for retrieving DHCPv6 shard configurations. + /// + /// @param servers_id list of shard configurations being populated + /// + /// @return true if shard configurations have been successfully retrieved, + /// false otherwise + bool getServersConfig6(std::vector& servers_id) const; + + /// @brief Deletes a single DHCPv4 shard configuration. + /// + /// @param instance_id identifier of server whose configuration is being + /// deleted + /// + /// @return true if the shard configuration has been successfully deleted, + /// false otherwise + bool deleteServerConfig4(const std::string& instance_id) const; + + /// @brief Deletes a single DHCPv6 shard configuration. + /// + /// @param instance_id identifier of server whose configuration is being + /// deleted + /// + /// @return true if the shard configuration has been successfully deleted, + /// false otherwise + bool deleteServerConfig6(const std::string& instance_id) const; + + /// @brief Server configuration specific tagged statements + static StatementMap tagged_statements_; + +private: + /// Database connection object + mutable CqlConnection dbconn_; +}; // CqlSrvConfigMasterMgr + +class CqlMasterConfigVersionExchange : public CqlVersionExchange { +public: + /// @brief Cassandra statements + static StatementMap tagged_statements_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // CQL_CONFIGURATION_MASTER_MGR_H diff --git a/src/lib/dhcpsrv/cql_srv_config_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_mgr.cc new file mode 100644 index 0000000000..bf4b1f9fb2 --- /dev/null +++ b/src/lib/dhcpsrv/cql_srv_config_mgr.cc @@ -0,0 +1,830 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Authors: Cristian Secareanu +// Andrei Pavel +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include +#include + +#include // for NULL +#include // for int32_t, int64_t + +#include // for BOOST_ASSERT +#include // for boost::uuids::random_generator +#include // for boost::uuid +#include + +#include // for std::string + +namespace isc { +namespace dhcp { + +StatementMap CqlConfigVersionExchange::tagged_statements_ = { + {GET_VERSION, // + {GET_VERSION, // + "SELECT " + "version, minor " + "FROM config_schema_version "}} // +}; + +class CqlConfigExchange : public virtual CqlExchange { +public: + /// @brief Statement tags definitions + /// @{ + // Get the timestamp of the v4 stored configuration + static constexpr StatementTag GET_CONFIGURATION4_TIMESTAMP = + "GET_CONFIGURATION4_TIMESTAMP"; + // Get the timestamp of the v6 stored configuration + static constexpr StatementTag GET_CONFIGURATION6_TIMESTAMP = + "GET_CONFIGURATION6_TIMESTAMP"; + // Get only GENERIC server configuration v4 + static constexpr StatementTag GET_GENERIC_CONFIGURATION4 = + "GET_GENERIC_CONFIGURATION4"; + // Get only GENERIC server configuration v6 + static constexpr StatementTag GET_GENERIC_CONFIGURATION6 = + "GET_GENERIC_CONFIGURATION6"; + // Get only JSON server configuration v4 + static constexpr StatementTag GET_JSON_CONFIGURATION4 = + "GET_JSON_CONFIGURATION4"; + // Get only JSON server configuration v6 + static constexpr StatementTag GET_JSON_CONFIGURATION6 = + "GET_JSON_CONFIGURATION6"; + // Add server configuration v4 + static constexpr StatementTag INSERT_CONFIGURATION4 = + "INSERT_CONFIGURATION4"; + // Add server configuration v6 + static constexpr StatementTag INSERT_CONFIGURATION6 = + "INSERT_CONFIGURATION6"; + // Update server configuration v4 + static constexpr StatementTag UPDATE_CONFIGURATION4 = + "UPDATE_CONFIGURATION4"; + // Update server configuration v6 + static constexpr StatementTag UPDATE_CONFIGURATION6 = + "UPDATE_CONFIGURATION6"; + /// @} + + /// @brief Constructor + /// + /// Specifies table columns. + CqlConfigExchange(); + + /// @brief Destructor + virtual ~CqlConfigExchange(); + + /// @brief Create BIND array to receive server configuration data. + /// + /// Used in executeSelect() to retrieve server configuration + /// from database + /// + /// @param data array of bound objects representing data to be retrieved. + /// @param statement_tag prepared statement being executed; defaults to an + /// invalid index + void createBindForSelect(AnyArray& data, + StatementTag statement_tag = NULL) override; + + /// @brief Copy received data into @ref SrvConfigInfo object + /// + /// Copies information about the shard configuration into a newly created + /// @ref SrvConfigInfo object. Called in @ref executeSelect(). + /// + /// @return pointer to a @ref SrvConfigInfoPtr object holding a + /// pointer to the @ref SrvConfigInfo object returned. + boost::any retrieve() override; + + /// @brief Generates a random UUID. + /// + /// @return string containing random UUID + std::string generateUuid(); + + /// @brief Common method of executing SELECT statements for the Cassandra + /// database. + /// + /// @param connection connection used to communicate with the Cassandra + /// database + /// @param statement_tag prepared Cassandra statement being executed + /// + /// @return true if statement has been [applied], false otherwise. + SrvConfigInfoCollection getCommon(CqlConnection& connection, + StatementTag statement_tag); + + /// @brief Common method of executing INSERT statements for the shard + /// database in the Cassandra database. + /// + /// @param connection connection used to communicate with the Cassandra + /// database + /// @param config_id identifier of the configuration being inserted + /// @param json_data JSON being inserted + /// @param generic_data generic data being inserted (e.g. yaml) + /// @param statement_tag prepared Cassandra statement being executed + /// + /// @return true if statement has been [applied], false otherwise. + bool insertCommon(CqlConnection& connection, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data, + StatementTag statement_tag); + + /// @brief Common method of executing SELECT statements for the Cassandra + /// database. + /// @param connection connection used to communicate with the Cassandra + /// database + /// @param config_id identifier of the configuration being inserted + /// @param old_timestamp old time stamp + /// @param json_data JSON being inserted + /// @param generic_data generic data being inserted (e.g. yaml) + /// @param statement_tag prepared Cassandra statement being executed + /// + /// @return true if statement has been [applied], false otherwise. + bool updateCommon(CqlConnection& connection, + const std::string& config_id, + const int64_t old_timestamp, + const std::string& json_data, + const std::string& generic_data, + StatementTag statement_tag); + + /// @brief Cassandra statements + static StatementMap tagged_statements_; + +private: + /// @brief Structure containing configuration data + SrvConfigInfoPtr config_data_; +}; // CqlConfigExchange + +constexpr StatementTag CqlConfigExchange::GET_CONFIGURATION4_TIMESTAMP; +constexpr StatementTag CqlConfigExchange::GET_CONFIGURATION6_TIMESTAMP; +constexpr StatementTag CqlConfigExchange::GET_GENERIC_CONFIGURATION4; +constexpr StatementTag CqlConfigExchange::GET_GENERIC_CONFIGURATION6; +constexpr StatementTag CqlConfigExchange::GET_JSON_CONFIGURATION4; +constexpr StatementTag CqlConfigExchange::GET_JSON_CONFIGURATION6; +constexpr StatementTag CqlConfigExchange::INSERT_CONFIGURATION4; +constexpr StatementTag CqlConfigExchange::INSERT_CONFIGURATION6; +constexpr StatementTag CqlConfigExchange::UPDATE_CONFIGURATION4; +constexpr StatementTag CqlConfigExchange::UPDATE_CONFIGURATION6; + +StatementMap CqlConfigExchange::tagged_statements_ = { + {GET_CONFIGURATION4_TIMESTAMP, // + {GET_CONFIGURATION4_TIMESTAMP, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_id, timestamp " + "FROM server_configuration4 " + "LIMIT 1 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // GET_CONFIGURATION6_TIMESTAMP + {GET_CONFIGURATION6_TIMESTAMP, // + {GET_CONFIGURATION6_TIMESTAMP, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_id, timestamp " + "FROM server_configuration6 " + "LIMIT 1 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // GET_GENERIC_CONFIGURATION4 + {GET_GENERIC_CONFIGURATION4, // + {GET_GENERIC_CONFIGURATION4, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_id, timestamp, generic_data " + "FROM server_configuration4 " + "LIMIT 1 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // GET_GENERIC_CONFIGURATION6 + {GET_GENERIC_CONFIGURATION6, // + {GET_GENERIC_CONFIGURATION6, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_id, timestamp, generic_data " + "FROM server_configuration6 LIMIT 1 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // GET_JSON_CONFIGURATION4 + {GET_JSON_CONFIGURATION4, // + {GET_JSON_CONFIGURATION4, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_id, timestamp, json_data " + "FROM server_configuration4 " + "LIMIT 1 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // GET_JSON_CONFIGURATION6 + {GET_JSON_CONFIGURATION6, // + {GET_JSON_CONFIGURATION6, // +#ifdef TERASTREAM_FULL_TRANSACTIONS + "@free = " +#endif // TERASTREAM_FULL_TRANSACTIONS + "SELECT config_id, timestamp, json_data " + "FROM server_configuration6 " + "LIMIT 1 " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // INSERT_CONFIGURATION4 + {INSERT_CONFIGURATION4, // + {INSERT_CONFIGURATION4, // + "INSERT INTO server_configuration4 " + "(config_id, timestamp, json_data, generic_data) " + "VALUES (?, ?, ?, ?) IF NOT EXISTS " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // INSERT_CONFIGURATION6 + {INSERT_CONFIGURATION6, // + {INSERT_CONFIGURATION6, // + "INSERT INTO server_configuration6 " + "(config_id, timestamp, json_data, generic_data) " + "VALUES (?, ?, ?, ?) IF NOT EXISTS " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // UPDATE_CONFIGURATION4 + {UPDATE_CONFIGURATION4, // + {UPDATE_CONFIGURATION4, // + "UPDATE server_configuration4 " + "SET timestamp = ?, json_data = ?, " + "generic_data = ? WHERE config_id = ? " + "IF timestamp = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }}, + + // UPDATE_CONFIGURATION6 + {UPDATE_CONFIGURATION6, // + {UPDATE_CONFIGURATION6, // + "UPDATE server_configuration6 " + "SET timestamp = ?, json_data = ?, " + "generic_data = ? WHERE config_id = ? " + "IF timestamp = ? " +#ifdef TERASTREAM_FULL_TRANSACTIONS + "IN TXN ? " +#endif // TERASTREAM_FULL_TRANSACTIONS + }} // +}; + +CqlConfigExchange::CqlConfigExchange() : config_data_(new SrvConfigInfo()) { +} + +CqlConfigExchange::~CqlConfigExchange() { +} + +void +CqlConfigExchange::createBindForSelect( + AnyArray& data, StatementTag statement_tag /* = NULL */) { + // Start with a fresh array. + data.clear(); + + data.add(&config_data_->config_id_); + data.add(&config_data_->timestamp_); + if (statement_tag == CqlConfigExchange::GET_JSON_CONFIGURATION4 || + statement_tag == CqlConfigExchange::GET_JSON_CONFIGURATION6) { + data.add(&config_data_->json_data_); + } else if (statement_tag == CqlConfigExchange::GET_GENERIC_CONFIGURATION4 || + statement_tag == CqlConfigExchange::GET_GENERIC_CONFIGURATION6) { + data.add(&config_data_->generic_data_); + } +} + +boost::any +CqlConfigExchange::retrieve() { + SrvConfigInfoPtr config(new SrvConfigInfo(*config_data_)); + return config; +} + +std::string +CqlConfigExchange::generateUuid() { + boost::uuids::random_generator gen; + boost::uuids::uuid unique_id = gen(); + std::string config_id = boost::lexical_cast(unique_id); + return config_id; +} + +SrvConfigInfoCollection +CqlConfigExchange::getCommon(CqlConnection& connection, + StatementTag statement_tag) { + + // Bind to array. + AnyArray where_values; + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = connection.getTransactionId(); + where_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + AnyArray collection = + executeSelect(connection, where_values, statement_tag, true); + + SrvConfigInfoCollection result; + + if (collection.empty()) { + return result; + } + + for (boost::any& element : collection) { + SrvConfigInfoPtr item(boost::any_cast(element)); + result.push_back(item); + } + + return result; +} + +bool +CqlConfigExchange::insertCommon(CqlConnection& connection, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data, + StatementTag statement_tag) { + + cass_int64_t config_timestamp = static_cast(time(NULL)); + + // Bind to array. + AnyArray assigned_values; + assigned_values.add(const_cast(&config_id)); + assigned_values.add(&config_timestamp); + assigned_values.add(const_cast(&json_data)); + assigned_values.add(const_cast(&generic_data)); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = connection.getTransactionId(); + assigned_values.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + executeMutation(connection, assigned_values, statement_tag); + + return true; +} + +bool +CqlConfigExchange::updateCommon(CqlConnection& connection, + const std::string& config_id, + const int64_t old_timestamp, + const std::string& json_data, + const std::string& generic_data, + StatementTag statement_tag) { + + // config_timestamp + cass_int64_t config_timestamp = static_cast(time(NULL)); + cass_int64_t old_config_timestamp = + static_cast(old_timestamp); + + // Bind to array. + AnyArray data; + data.add(&config_timestamp); + data.add(const_cast(&json_data)); + data.add(const_cast(&generic_data)); + data.add(const_cast(&config_id)); + data.add(&old_config_timestamp); + +#ifdef TERASTREAM_FULL_TRANSACTIONS + CassUuid txid = connection.getTransactionId(); + data.add(&txid); +#endif // TERASTREAM_FULL_TRANSACTIONS + + executeMutation(connection, data, statement_tag); + + return true; +} + +CqlSrvConfigMgr::CqlSrvConfigMgr( + const DatabaseConnection::ParameterMap& parameters) + : SrvConfigMgr(), dbconn_(parameters) { + dbconn_.openDatabase(); +#ifdef TERASTREAM_FULL_TRANSACTIONS + dbconn_.setTransactionOperations(CqlTransactionExchange::BEGIN_TXN, + CqlTransactionExchange::COMMIT_TXN, + CqlTransactionExchange::ROLLBACK_TXN); +#endif // TERASTREAM_FULL_TRANSACTIONS + dbconn_.prepareStatements(CqlConfigExchange::tagged_statements_); + dbconn_.prepareStatements(CqlConfigVersionExchange::tagged_statements_); +#ifdef TERASTREAM_FULL_TRANSACTIONS + dbconn_.prepareStatements(CqlTransactionExchange::tagged_statements_); +#endif // TERASTREAM_FULL_TRANSACTIONS +} + +CqlSrvConfigMgr::~CqlSrvConfigMgr() { +} + +SrvConfigInfoPtr +CqlSrvConfigMgr::getConfig4Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_CONFIG4_TIMESTAMP); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + SrvConfigInfoCollection collection = config_exchange->getCommon( + dbconn_, CqlConfigExchange::GET_CONFIGURATION4_TIMESTAMP); + + transaction.commit(); + + SrvConfigInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +SrvConfigInfoPtr +CqlSrvConfigMgr::getConfig6Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_CONFIG6_TIMESTAMP); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + SrvConfigInfoCollection collection = config_exchange->getCommon( + dbconn_, CqlConfigExchange::GET_CONFIGURATION6_TIMESTAMP); + + transaction.commit(); + + SrvConfigInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +SrvConfigInfoPtr +CqlSrvConfigMgr::getGenericConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_CONFIG4_GENERIC); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + SrvConfigInfoCollection collection = config_exchange->getCommon( + dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION4); + + transaction.commit(); + + SrvConfigInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +SrvConfigInfoPtr +CqlSrvConfigMgr::getGenericConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_CONFIG6_GENERIC); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + SrvConfigInfoCollection collection = config_exchange->getCommon( + dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION6); + + transaction.commit(); + + SrvConfigInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +SrvConfigInfoPtr +CqlSrvConfigMgr::getJsonConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_CONFIG4_JSON); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + SrvConfigInfoCollection collection = config_exchange->getCommon( + dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION4); + + transaction.commit(); + + SrvConfigInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +SrvConfigInfoPtr +CqlSrvConfigMgr::getJsonConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_SRV_CONFIG6_JSON); + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + SrvConfigInfoCollection collection = config_exchange->getCommon( + dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION6); + + transaction.commit(); + + SrvConfigInfoPtr result; + if (!collection.empty()) { + result = *collection.begin(); + } + + return result; +} + +std::string +CqlSrvConfigMgr::getType() const { + return std::string("cql"); +} + +VersionPair +CqlSrvConfigMgr::getVersion() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_VERSION); + + std::unique_ptr version_exchange( + new CqlVersionExchange()); + return version_exchange->retrieveVersion(dbconn_); +} + +std::string +CqlSrvConfigMgr::getDBVersion() { + std::stringstream tmp; + tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR; + tmp << "." << CQL_SCHEMA_VERSION_MINOR; + tmp << ", library cassandra_static " << CASS_VERSION_MAJOR; + tmp << "." << CASS_VERSION_MINOR; + tmp << "." << CASS_VERSION_PATCH; + return tmp.str(); +} + +bool +CqlSrvConfigMgr::updateConfig4(const int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG4); + + // Initiate CQL transaction as we will have to update the config + // from two steps and we don't want anybody else to write another + // config between these two steps: + // Step 1: Read the current configuration timestamp from database. + // Step 2: Check if the provided configuration timestamp is + // the same with the existing one from database. + // Step 3: Update the configuration on the server. + // If there is no configuration on the server then insert a new one but + // also using transactions because we don't want another user to insert also + // a new configuration in the same time. + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + SrvConfigInfoPtr current_config = getConfig4Timestamp(); + + if (!current_config) { + if (insertConfig4(json_data, generic_data)) { + transaction.commit(); + return true; + } + return false; + } + + if (!updateConfig4(current_config->config_id_, config_timestamp, json_data, + generic_data)) { + // The configuration timestamp has been changed since the last + // configuration read until this update. + + LOG_WARN(dhcpsrv_logger, + DHCPSRV_CQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED) + .arg(config_timestamp) + .arg(current_config->timestamp_); + + return false; + } + + transaction.commit(); + + return true; +} + +bool +CqlSrvConfigMgr::updateConfig6(const int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG6); + + // Initiate CQL transaction as we will have to update the config + // from two steps and we don't want anybody else to write another + // config between these two steps: + // Step 1: Read the current configuration timestamp from database. + // Step 2: Check if the provided configuration timestamp is + // the same with the existing one from database. + // Step 3: Update the configuration on the server. + // If there is no configuration on the server then insert a new one but + // also using transactions because we don't want another user to insert also + // a new configuration in the same time. + + // CqlTransaction rolls back the transaction on its destructor. + + // Begin the transaction. + CqlTransaction transaction(dbconn_); + + SrvConfigInfoPtr current_config = getConfig6Timestamp(); + + if (!current_config) { + if (insertConfig6(json_data, generic_data)) { + transaction.commit(); + return true; + } + return false; + } + + if (!updateConfig6(current_config->config_id_, config_timestamp, json_data, + generic_data)) { + // The configuration timestamp has been changed since the last + // configuration read until this update. + LOG_WARN(dhcpsrv_logger, + DHCPSRV_CQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED) + .arg(config_timestamp) + .arg(current_config->timestamp_); + + return false; + } + + transaction.commit(); + + return true; +} + +bool +CqlSrvConfigMgr::insertConfig4(const std::string& json_data, + const std::string& generic_data) const { + std::unique_ptr config_exchange(new CqlConfigExchange()); + const std::string config_id = config_exchange->generateUuid(); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_INSERT_SRV_CONFIG4) + .arg(config_id); + + try { + + config_exchange->insertCommon(dbconn_, config_id, json_data, + generic_data, + CqlConfigExchange::INSERT_CONFIGURATION4); + + } catch (const Exception&) { + return false; + } + + return true; +} + +bool +CqlSrvConfigMgr::insertConfig6(const std::string& json_data, + const std::string& generic_data) const { + std::unique_ptr config_exchange(new CqlConfigExchange()); + const std::string config_id = config_exchange->generateUuid(); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_INSERT_SRV_CONFIG6) + .arg(config_id); + + try { + config_exchange->insertCommon(dbconn_, config_id, json_data, + generic_data, + CqlConfigExchange::INSERT_CONFIGURATION6); + } catch (const Exception&) { + return false; + } + + return true; +} + +bool +CqlSrvConfigMgr::updateConfig4(const std::string& config_id, + const int64_t old_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_UPDATE_SRV_CONFIG4) + .arg(config_id); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + try { + config_exchange->updateCommon(dbconn_, config_id, old_timestamp, + json_data, generic_data, + CqlConfigExchange::UPDATE_CONFIGURATION4); + } catch (const Exception&) { + return false; + } + + return true; +} + +bool +CqlSrvConfigMgr::updateConfig6(const std::string& config_id, + const int64_t old_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_UPDATE_SRV_CONFIG6) + .arg(config_id); + + std::unique_ptr config_exchange(new CqlConfigExchange()); + try { + config_exchange->updateCommon(dbconn_, config_id, old_timestamp, + json_data, generic_data, + CqlConfigExchange::UPDATE_CONFIGURATION6); + } catch (const Exception&) { + return false; + } + + return true; +} + +bool +CqlSrvConfigMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_BEGIN_TRANSACTION); + dbconn_.startTransaction(); + return true; +} + +void +CqlSrvConfigMgr::commit() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT); + dbconn_.commit(); +} + +void +CqlSrvConfigMgr::rollback() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK); + dbconn_.rollback(); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cql_srv_config_mgr.h b/src/lib/dhcpsrv/cql_srv_config_mgr.h new file mode 100644 index 0000000000..9f48d4527a --- /dev/null +++ b/src/lib/dhcpsrv/cql_srv_config_mgr.h @@ -0,0 +1,207 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Authors: Cristian Secareanu +// Andrei Pavel +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CQL_CONFIGURATION_MGR_H +#define CQL_CONFIGURATION_MGR_H + +#include +#include +#include + +#include + +#include +#include + +namespace isc { +namespace dhcp { + +class CqlConfigExchange; + +class CqlSrvConfigMgr : public SrvConfigMgr { +public: + /// @brief Constructor + explicit CqlSrvConfigMgr( + const DatabaseConnection::ParameterMap& parameters); + + /// @brief Destructor + virtual ~CqlSrvConfigMgr(); + + /// @brief Retrieves IPv4 timestamp. + virtual SrvConfigInfoPtr getConfig4Timestamp() const override; + + /// @brief Retrieves IPv6 timestamp. + virtual SrvConfigInfoPtr getConfig6Timestamp() const override; + + /// @brief Retrieves IPv4 configuration in generic format (e.g. yaml). + virtual SrvConfigInfoPtr getGenericConfig4() const override; + + /// @brief Retrieves IPv6 configuration in generic format (e.g. yaml). + virtual SrvConfigInfoPtr getGenericConfig6() const override; + + /// @brief Retrieves IPv4 configuration in JSON format. + virtual SrvConfigInfoPtr getJsonConfig4() const override; + + /// @brief Retrieves IPv6 configuration in JSON format. + virtual SrvConfigInfoPtr getJsonConfig6() const override; + + /// @brief Retrieves database type + virtual std::string getType() const override; + + /// @brief Retrieves schema version + virtual VersionPair getVersion() const override; + + /// @brief Local version of getDBVersion() class method + static std::string getDBVersion(); + + /// @brief Adds if not exists or updates an existing DHCP V4 server + /// configuration + /// + /// One database contains server configuration data only for one Kea server. + /// + /// @param old_config_timestamp If there is already a server configuration + /// in the database then the configuration's timestamp from database is + /// compared with the provided one. The update takes place only if the + /// timestamps are the same. + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @result true if the configuration was added/updated, false if not. + virtual bool updateConfig4(const int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const override; + + /// @brief Adds if not exists or updates an existing DHCP V6 server + /// configuration + /// + /// One database contains server configuration data only for one Kea server. + /// + /// @param old_config_timestamp If there is already a server configuration + /// in the database then the configuration's timestamp from database is + /// compared with the provided one. The update takes place only if the + /// timestamps are the same. + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @result true if the configuration was added/updated, false if not. + virtual bool updateConfig6(const int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const override; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() override; + + /// @brief Commit Transactions + /// + /// This is a no-op for Cassandra. + virtual void commit() override; + + /// @brief Rollback Transactions + /// + /// This is a no-op for Cassandra. + virtual void rollback() override; + +private: + /// @brief Inserts a new DHCPv4 server configuration. + /// + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be a + /// CQL error. + /// + /// @return true if the configuration was added, false if not. + bool insertConfig4(const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Inserts a new DHCPv6 server configuration. + /// + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be a + /// CQL error. + /// + /// @return true if the configuration was added, false if not. + bool insertConfig6(const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Updates an existing DHCPv4 server configuration. + /// + /// + /// @param version the new configuration's version. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be a + /// CQL error. + /// + /// @return true if the configuration was added/updated, false if not. + bool updateConfig4(const std::string& config_id, + const int64_t timestamp, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Updates an existing DHCPv6 server configuration. + /// + /// + /// @param version the new configuration's version. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be a + /// CQL error. + /// + /// @return true if the configuration was added/updated, false if not. + bool updateConfig6(const std::string& config_id, + const int64_t timestamp, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Server configuration specific tagged statements + static CqlTaggedStatement tagged_statements_[]; + /// @brief Database connection object + mutable CqlConnection dbconn_; +}; + +class CqlConfigVersionExchange : public CqlVersionExchange { +public: + /// @brief Cassandra statements + static StatementMap tagged_statements_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // CQL_CONFIGURATION_MGR_H diff --git a/src/lib/dhcpsrv/database_connection.h b/src/lib/dhcpsrv/database_connection.h index 8dcbe56115..794d1dce04 100644 --- a/src/lib/dhcpsrv/database_connection.h +++ b/src/lib/dhcpsrv/database_connection.h @@ -230,7 +230,7 @@ class DatabaseConnection : public boost::noncopyable { }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // end of isc::dhcp namespace +} // end of isc namespace #endif // DATABASE_CONNECTION_H diff --git a/src/lib/dhcpsrv/db_log.cc b/src/lib/dhcpsrv/db_log.cc index c41664524f..f2e3cd0a42 100644 --- a/src/lib/dhcpsrv/db_log.cc +++ b/src/lib/dhcpsrv/db_log.cc @@ -6,6 +6,8 @@ /// Defines the logger used by the NSAS +#include + #include #include #include diff --git a/src/lib/dhcpsrv/db_type.h b/src/lib/dhcpsrv/db_type.h index dc22fcd158..db00e83ea5 100644 --- a/src/lib/dhcpsrv/db_type.h +++ b/src/lib/dhcpsrv/db_type.h @@ -13,7 +13,9 @@ namespace dhcp { /// @brief Specifies the database type. enum class DBType { LEASE_DB = 1, - HOSTS_DB = 2 + HOSTS_DB = 2, + CONFIG_DB = 3, + MASTER_DB = 4 }; } // namespace isc diff --git a/src/lib/dhcpsrv/dhcpsrv_db_log.cc b/src/lib/dhcpsrv/dhcpsrv_db_log.cc index b39585edd6..013d8c469d 100644 --- a/src/lib/dhcpsrv/dhcpsrv_db_log.cc +++ b/src/lib/dhcpsrv/dhcpsrv_db_log.cc @@ -6,6 +6,8 @@ /// Defines the logger used by the NSAS +#include + #include #include diff --git a/src/lib/dhcpsrv/dhcpsrv_log.cc b/src/lib/dhcpsrv/dhcpsrv_log.cc index ea2231c3be..065fe177c2 100644 --- a/src/lib/dhcpsrv/dhcpsrv_log.cc +++ b/src/lib/dhcpsrv/dhcpsrv_log.cc @@ -13,8 +13,14 @@ namespace isc { namespace dhcp { +const int DHCPSRV_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +const int DHCPSRV_DBG_RESULTS = isc::log::DBGLVL_TRACE_BASIC_DATA; +const int DHCPSRV_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +const int DHCPSRV_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; +const int DHCPSRV_DBG_HOOKS = isc::log::DBGLVL_TRACE_BASIC; + isc::log::Logger dhcpsrv_logger("dhcpsrv"); -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/dhcpsrv_log.h b/src/lib/dhcpsrv/dhcpsrv_log.h index b4c41b1cb2..6af21be51c 100644 --- a/src/lib/dhcpsrv/dhcpsrv_log.h +++ b/src/lib/dhcpsrv/dhcpsrv_log.h @@ -23,27 +23,27 @@ namespace dhcp { /// @brief Traces normal operations /// /// E.g. sending a query to the database etc. -const int DHCPSRV_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +extern const int DHCPSRV_DBG_TRACE; /// @brief Records the results of the lookups /// /// Using the example of tracing queries from the backend database, this will /// just record the summary results. -const int DHCPSRV_DBG_RESULTS = isc::log::DBGLVL_TRACE_BASIC_DATA; +extern const int DHCPSRV_DBG_RESULTS; /// @brief Additional information /// /// Record detailed tracing. This is generally reserved for tracing access to /// the lease database. -const int DHCPSRV_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +extern const int DHCPSRV_DBG_TRACE_DETAIL; /// @brief Additional information /// /// Record detailed (and verbose) data on the server. -const int DHCPSRV_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; +extern const int DHCPSRV_DBG_TRACE_DETAIL_DATA; // Trace hook related operations -const int DHCPSRV_DBG_HOOKS = isc::log::DBGLVL_TRACE_BASIC; +extern const int DHCPSRV_DBG_HOOKS; ///@} @@ -55,7 +55,7 @@ const int DHCPSRV_DBG_HOOKS = isc::log::DBGLVL_TRACE_BASIC; /// space. extern isc::log::Logger dhcpsrv_logger; -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // DHCPSRV_LOG_H diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 8d6e4fa36e..b9b286a2d1 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -301,6 +301,104 @@ information from the Cassandra database. An informational message logged when options belonging to any reservation from a single host are inserted. +% DHCPSRV_CQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 server configuration with ID %1 +A debug message issued when there is no existing DHCPv4 server configuration in +database and a configuration will be inserted. + +% DHCPSRV_CQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 server configuration with ID %1 +A debug message issued when there is no existing DHCPv6 server configuration in +database and a configuration will be inserted. + +% DHCPSRV_CQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with ID %1. +A debug message issued when there already exists a DHCPv4 server configuration +in database and this configuration is going to be updated. + +% DHCPSRV_CQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with ID %1. +A debug message issued when there already exists a DHCPv6 server configuration +in database and this configuration is going to be updated. + +% DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the DHCPv4 database server configuration +A debug message issued when a DHCPv4 server configuration database update request is received. +If there already exists a configuration in the database then the configuration will be updated. +Otherwise a new configuration will be inserted. + +% DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the DHCPv6 database server configuration +A debug message issued when a DHCPv6 server configuration database update request is received. +If there already exists a configuration in the database then the configuration will be updated. +Otherwise a new configuration will be inserted. + +% DHCPSRV_CQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the DHCPv4 database server configuration (old timestamp is %1, new timestamp is %2) +A warning message issued when DHCPv4 database server configuration cannot be updated. +On a database server configuration update the actual timestamp of the configuration is also provided to the server. +If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails. + +% DHCPSRV_CQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the DHCPv6 database server configuration (old timestamp is %1, new timestamp is %2) +A warning message issued when DHCPv6 database server configuration cannot be updated. +On a database server configuration update the actual timestamp of the configuration is also provided to the server. +If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails. + +% DHCPSRV_CQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's version from database +Obtains the DHCPv4 server configuration's version from database. + +% DHCPSRV_CQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's version from database +Obtains the DHCPv6 server configuration's version from database. + +% DHCPSRV_CQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database +Obtains the DHCPv4 server JSON configuration from database. + +% DHCPSRV_CQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database +Obtains the DHCPv6 server JSON configuration from database. + +% DHCPSRV_CQL_GET_SRV_CONFIG4_GENERIC Obtaining the DHCPv4 server GENERIC configuration from database +Obtains the DHCPv4 server GENERIC configuration from database. + +% DHCPSRV_CQL_GET_SRV_CONFIG6_GENERIC Obtaining the DHCPv6 server GENERIC configuration from database +Obtains the DHCPv6 server GENERIC configuration from database. + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database ID %1 +Obtains the DHCPv4 server configuration from the master database. + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database ID %1 +Obtains the DHCPv6 server configuration from the master database. + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database +Obtains the DHCPv4 server configuration from the master database for the specified shard database. + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database +Obtains the DHCPv6 server configuration from the master database for the specified shard database. + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database +Obtains the DHCPv4 shards name from the master database + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database +Obtains the DHCPv6 shards name from the master database + +% DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database +Clears the DHCPv4 master configuration from database + +% DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database +Clears the DHCPv6 master configuration from database + +% DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG4 Deleting the DHCPv4 master configuration from database ID %1 +Clears the DHCPv4 master configuration from database + +% DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG6 Deleting the DHCPv6 master configuration from database ID %1 +Clears the DHCPv6 master configuration from database + +% DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with ID %1 and shard name %2 +A debug message issued when there is no existing DHCPv4 master server configuration in +database and a configuration will be inserted. + +% DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with ID %1 and shard name %2 +A debug message issued when there is no existing DHCPv6 master server configuration in +database and a configuration will be inserted. + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database ID %1 +Obtains the DHCPv4 master server configuration's version from database. + +% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database ID %1 +Obtains the DHCPv6 master server configuration's version from database. + % DHCPSRV_CQL_HOST_DB Connecting to CQL hosts database: %1 An informational message logged when the CQL hosts database is about to be connected to. The parameters of the connection including database name and @@ -318,6 +416,8 @@ host from a CQL database by IPv4 criteria. An informational message logged when a DHCP server is about to retrieve one host from a CQL database by IPv6 criteria. +% DHCPSRV_CQL_HOST_SYNC_RESERVATIONS Syncing reservations in database with kea.conf. + % DHCPSRV_CQL_HOST_GET_ALL Retrieving multiple hosts from a CQL database An informational message logged when multiple hosts from a CQL database are retrieved. @@ -822,6 +922,96 @@ lease from the MySQL database for the specified address. A debug message issued when the server is attempting to update IPv6 lease from the MySQL database for the specified address. +% DHCPSRV_MYSQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 configuration with ID %1 +A debug message issued when there is no existing DHCPv4 server configuration in database and a configuration will be inserted. + +% DHCPSRV_MYSQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 configuration with ID %1 +A debug message issued when there is no existing DHCPv6 server configuration in database and a configuration will be inserted. + +% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with ID %1. +A debug message issued when there already exists a DHCPv4 server configuration in database +and this configuration is going to be updated. + +% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with ID %1. +A debug message issued when there already exists a DHCPv6 server configuration in database +and this configuration is going to be updated. + +% DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the DHCPv4 database server configuration +A debug message issued when a DHCPv4 configuration database update request is received. +If there already exists a configuration in the database then the configuration will be updated. +Otherwise a new configuration will be inserted. + +% DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the DHCPv6 database server configuration +A debug message issued when a DHCPv6 configuration database update request is received. +If there already exists a configuration in the database then the configuration will be updated. +Otherwise a new configuration will be inserted. + +% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the DHCP4 database server configuration (old timestamp is %1, new timestamp is %2) +A warning message issued when DHCP4 database server configuration cannot be updated. +On a database server configuration update the actual timestamp of the configuration is also provided to the server. +If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails. + +% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the DHCPv6 database server configuration (old timestamp is %1, new timestamp is %2) +A warning message issued when DHCPv6 database server configuration cannot be updated. +On a database server configuration update the actual timestamp of the configuration is also provided to the server. +If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails. + +% DHCPSRV_MYSQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's timestamp from database +Obtains the DHCPv4 server configuration's timestamp from database. + +% DHCPSRV_MYSQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's timestamp from database +Obtains the DHCPv6 server configuration's timestamp from database. + +% DHCPSRV_MYSQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database +Obtains the DHCPv4 server JSON configuration from database. + +% DHCPSRV_MYSQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database +Obtains the DHCPv6 server JSON configuration from database. + +% DHCPSRV_MYSQL_GET_SRV_CONFIG4_GENERIC Obtaining the DCHPv4 server GENERIC configuration from database +Obtains the DCHPv4 server GENERIC configuration from database. + +% DHCPSRV_MYSQL_GET_SRV_CONFIG6_GENERIC Obtaining the DCHPv6 server GENERIC configuration from database +Obtains the DCHPv6 server GENERIC configuration from database. + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database ID %1 +Obtains the DHCPv4 server configuration from the master database. + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database ID %1 +Obtains the DHCPv6 server configuration from the master database. + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database +Obtains the DHCPv4 server configuration from the master database for the specified shard database. + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database +Obtains the DHCPv6 server configuration from the master database for the specified shard database. + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database +Obtains the DHCPv4 shards name from the master database + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database +Obtains the DHCPv6 shards name from the master database + +% DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database +Clears the DHCPv4 master configuration from database + +% DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database +Clears the DHCPv6 master configuration from database + +% DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with ID %1 and shard name %2 +A debug message issued when there is no existing DHCPv4 master server configuration in +database and a configuration will be inserted. + +% DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with ID %1 and shard name %2 +A debug message issued when there is no existing DHCPv6 master server configuration in +database and a configuration will be inserted. + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database ID %1 +Obtains the DHCPv4 master server configuration's version from database. + +% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database ID %1 +Obtains the DHCPv6 master server configuration's version from database. + % DHCPSRV_NOTYPE_DB no 'type' keyword to determine database backend: %1 This is an error message, logged when an attempt has been made to access a database backend, but where no 'type' keyword has been included in @@ -994,6 +1184,96 @@ lease from the PostgreSQL database for the specified address. A debug message issued when the server is attempting to update IPv6 lease from the PostgreSQL database for the specified address. +% DHCPSRV_PGSQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 server configuration with ID %1 +A debug message issued when there is no existing DHCPv4 server configuration in database and a configuration will be inserted. + +% DHCPSRV_PGSQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 server configuration with ID %1 +A debug message issued when there is no existing DHCPv6 server configuration in database and a configuration will be inserted. + +% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with ID %1. +A debug message issued when there already exists a DHCPv4 server configuration in database +and this configuration is going to be updated. + +% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with ID %1. +A debug message issued when there already exists a DHCPv6 server configuration in database +and this configuration is going to be updated. + +% DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the IPv4 database server configuration +A debug message issued when a IPv4 server configuration database update request is received. +If there already exists a configuration in the database then the configuration will be updated. +Otherwise a new configuration will be inserted. + +% DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the IPv6 database server configuration +A debug message issued when a IPv6 server configuration database update request is received. +If there already exists a configuration in the database then the configuration will be updated. +Otherwise a new configuration will be inserted. + +% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the IPv4 database server configuration (old timestamp is %1, new timestamp is %2) +A warning message issued when IPv4 database server configuration cannot be updated. +On a database server configuration update the actual timestamp of the configuration is also provided to the server. +If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails. + +% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the IPv6 database server configuration (old timestamp is %1, new timestamp is %2) +A warning message issued when IPv6 database server configuration cannot be updated. +On a database server configuration update the actual timestamp of the configuration is also provided to the server. +If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails. + +% DHCPSRV_PGSQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's version from database +Obtains the DHCPv4 server configuration's version from database. + +% DHCPSRV_PGSQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's version from database +Obtains the DHCPv6 server configuration's version from database. + +% DHCPSRV_PGSQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database +Obtains the DHCPv4 server JSON configuration from database. + +% DHCPSRV_PGSQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database +Obtains the DHCPv6 server JSON configuration from database. + +% DHCPSRV_PGSQL_GET_SRV_CONFIG4_GENERIC Obtaining the DHCPv4 server GENERIC configuration from database +Obtains the DHCPv4 server GENERIC configuration from database. + +% DHCPSRV_PGSQL_GET_SRV_CONFIG6_GENERIC Obtaining the DHCPv6 server GENERIC configuration from database +Obtains the DHCPv6 server GENERIC configuration from database. + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database ID %1 +Obtains the DHCPv4 server configuration from the master database. + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database ID %1 +Obtains the DHCPv6 server configuration from the master database. + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database +Obtains the DHCPv4 server configuration from the master database for the specified shard database. + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database +Obtains the DHCPv6 server configuration from the master database for the specified shard database. + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database +Obtains the DHCPv4 shards name from the master database + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database +Obtains the DHCPv6 shards name from the master database + +% DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database +Clears the DHCPv4 master configuration from database + +% DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database +Clears the DHCPv6 master configuration from database + +% DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with ID %1 and shard name %2 +A debug message issued when there is no existing DHCPv4 master server configuration in +database and a configuration will be inserted. + +% DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with ID %1 and shard name %2 +A debug message issued when there is no existing DHCPv6 master server configuration in +database and a configuration will be inserted. + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database ID %1 +Obtains the DHCPv4 master server configuration's version from database. + +% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database ID %1 +Obtains the DHCPv6 master server configuration's version from database. + % DHCPSRV_QUEUE_NCR %1: name change request to %2 DNS entry queued: %3 A debug message which is logged when the NameChangeRequest to add or remove a DNS entries for a particular lease has been queued. The first argument diff --git a/src/lib/dhcpsrv/host_container.h b/src/lib/dhcpsrv/host_container.h index e352051f36..ba2513cc3a 100644 --- a/src/lib/dhcpsrv/host_container.h +++ b/src/lib/dhcpsrv/host_container.h @@ -191,7 +191,7 @@ typedef HostContainer6::nth_index<1>::type HostContainer6Index1; typedef std::pair HostContainer6Index1Range; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // end of isc::dhcp namespace +} // end of isc namespace #endif // HOST_CONTAINER_H diff --git a/src/lib/dhcpsrv/hosts_log.cc b/src/lib/dhcpsrv/hosts_log.cc index fbcb97e197..7f49dc26ad 100644 --- a/src/lib/dhcpsrv/hosts_log.cc +++ b/src/lib/dhcpsrv/hosts_log.cc @@ -13,8 +13,13 @@ namespace isc { namespace dhcp { +const int HOSTS_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +const int HOSTS_DBG_RESULTS = isc::log::DBGLVL_TRACE_BASIC_DATA; +const int HOSTS_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +const int HOSTS_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; + isc::log::Logger hosts_logger("hosts"); -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/hosts_log.h b/src/lib/dhcpsrv/hosts_log.h index e4b1faf62d..cbd9b0c932 100644 --- a/src/lib/dhcpsrv/hosts_log.h +++ b/src/lib/dhcpsrv/hosts_log.h @@ -24,23 +24,23 @@ namespace dhcp { /// /// An example of the normal operation is the call to one of the functions /// which retrieve the reservations or add new reservation. -const int HOSTS_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +extern const int HOSTS_DBG_TRACE; /// @brief Records the results of the lookups /// /// Messages logged at this level will typically contain summary of the /// data retrieved. -const int HOSTS_DBG_RESULTS = isc::log::DBGLVL_TRACE_BASIC_DATA; +extern const int HOSTS_DBG_RESULTS; /// @brief Record detailed traces /// /// Messages logged at this level will log detailed tracing information. -const int HOSTS_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; +extern const int HOSTS_DBG_TRACE_DETAIL; /// @brief Records detailed results of lookups. /// /// Messages logged at this level will contain detailed results. -const int HOSTS_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; +extern const int HOSTS_DBG_TRACE_DETAIL_DATA; ///@} @@ -50,7 +50,7 @@ const int HOSTS_DBG_TRACE_DETAIL_DATA = isc::log::DBGLVL_TRACE_DETAIL_DATA; /// calls to manage host reservations. extern isc::log::Logger hosts_logger; -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // HOSTS_LOG_H diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc index 9f52c6603d..fc2e338a78 100644 --- a/src/lib/dhcpsrv/lease.cc +++ b/src/lib/dhcpsrv/lease.cc @@ -702,5 +702,5 @@ operator<<(std::ostream& os, const Lease& lease) { return (os); } -} // namespace isc::dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index d8869b5af8..2ad44181a4 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -463,7 +463,6 @@ typedef boost::shared_ptr Lease6Ptr; /// would be required. As this is a critical part of the code that will be used /// extensively, direct access is warranted. struct Lease6 : public Lease { - /// @brief Lease type /// /// One of normal address, temporary address, or prefix. @@ -610,7 +609,7 @@ typedef boost::shared_ptr Lease6CollectionPtr; std::ostream& operator<<(std::ostream& os, const Lease& lease); -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc #endif // LEASE_H diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc index 1defefc259..4fcc5c9b70 100644 --- a/src/lib/dhcpsrv/lease_mgr.cc +++ b/src/lib/dhcpsrv/lease_mgr.cc @@ -277,5 +277,5 @@ LeaseMgr::getDBVersion() { isc_throw(NotImplemented, "LeaseMgr::getDBVersion() called"); } -} // namespace isc::dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index a820304ae2..90259a1389 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -61,9 +61,6 @@ namespace isc { namespace dhcp { -/// @brief Pair containing major and minor versions -typedef std::pair VersionPair; - /// @brief Contains a single row of lease statistical data /// /// The contents of the row consist of a subnet ID, a lease @@ -220,6 +217,9 @@ typedef boost::shared_ptr LeaseStatsQueryPtr; /// @brief Defines a pointer to a LeaseStatsRow. typedef boost::shared_ptr LeaseStatsRowPtr; +/// @brief Collection of the @c LeaseStatsRow objects. +typedef std::vector LeaseStatsCollection; + /// @brief Abstract Lease Manager /// /// This is an abstract API for lease database backends. It provides unified @@ -667,6 +667,12 @@ class LeaseMgr { /// Also if B>C, some database upgrade procedure may be triggered virtual VersionPair getVersion() const = 0; + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() = 0; + /// @brief Commit Transactions /// /// Commits all pending database operations. On databases that don't diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index 449dae4564..dfe1627732 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -36,7 +36,7 @@ const uint32_t MAX_LEASE_ERRORS = 100; /// Kea installation directory. const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE"; -} // end of anonymous namespace +} // namespace using namespace isc::util; @@ -1218,6 +1218,12 @@ Memfile_LeaseMgr::getDescription() const { return (std::string("In memory database with leases stored in a CSV file.")); } +bool +Memfile_LeaseMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_BEGIN_TRANSACTION); + return true; +} + void Memfile_LeaseMgr::commit() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT); @@ -1582,6 +1588,6 @@ size_t Memfile_LeaseMgr::wipeLeases6(const SubnetID& subnet_id) { return (num); } +} // namespace dhcp +} // namespace isc -} // end of namespace isc::dhcp -} // end of namespace isc diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index 3af59246a8..c7b2a4d317 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -439,7 +439,7 @@ class Memfile_LeaseMgr : public LeaseMgr { /// /// @return Version number as a pair of unsigned integers. "first" is the /// major version number, "second" the minor number. - virtual std::pair getVersion() const { + virtual VersionPair getVersion() const { return (std::make_pair(MAJOR_VERSION, MINOR_VERSION)); } @@ -779,7 +779,7 @@ class Memfile_LeaseMgr : public LeaseMgr { //@} }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc #endif // MEMFILE_LEASE_MGR_H diff --git a/src/lib/dhcpsrv/mysql_connection.cc b/src/lib/dhcpsrv/mysql_connection.cc index a4de082a5f..e2cfb577d6 100644 --- a/src/lib/dhcpsrv/mysql_connection.cc +++ b/src/lib/dhcpsrv/mysql_connection.cc @@ -42,7 +42,10 @@ MySqlTransaction::~MySqlTransaction() { // Rollback if the MySqlTransaction::commit wasn't explicitly // called. if (!committed_) { - conn_.rollback(); + try { + conn_.rollback(); + } catch (...) { + } } } @@ -131,10 +134,8 @@ MySqlConnection::openDatabase() { // No timeout parameter, we are going to use the default timeout. stimeout = ""; } - if (stimeout.size() > 0) { // Timeout was given, so try to convert it to an integer. - try { connect_timeout = boost::lexical_cast(stimeout); } catch (...) { @@ -399,5 +400,5 @@ MySqlConnection::rollback() { } -} // namespace isc::dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/mysql_connection.h b/src/lib/dhcpsrv/mysql_connection.h index b1d22d090b..1b70a74867 100644 --- a/src/lib/dhcpsrv/mysql_connection.h +++ b/src/lib/dhcpsrv/mysql_connection.h @@ -436,7 +436,7 @@ class MySqlConnection : public DatabaseConnection { MySqlHolder mysql_; }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc #endif // MYSQL_CONNECTION_H diff --git a/src/lib/dhcpsrv/mysql_host_data_source.cc b/src/lib/dhcpsrv/mysql_host_data_source.cc index 1c72afd741..ca6af6c84b 100644 --- a/src/lib/dhcpsrv/mysql_host_data_source.cc +++ b/src/lib/dhcpsrv/mysql_host_data_source.cc @@ -9,12 +9,11 @@ #include #include #include -#include #include +#include #include #include #include -#include #include #include @@ -27,8 +26,9 @@ #include #include -#include +#include #include +#include using namespace isc; using namespace isc::asiolink; @@ -1883,7 +1883,7 @@ class MySqlOptionExchange { MYSQL_BIND bind_[OPTION_COLUMNS]; }; -} // end of anonymous namespace +} // namespace namespace isc { namespace dhcp { @@ -2218,7 +2218,6 @@ TaggedStatementArray tagged_statements = { { "h.dhcp_identifier_type, h.dhcp4_subnet_id, " "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, " "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, " - "h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, " "o.option_id, o.code, o.value, o.formatted_value, o.space, " "o.persistent, o.user_context, " @@ -2269,11 +2268,11 @@ TaggedStatementArray tagged_statements = { { "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"}, {MySqlHostDataSourceImpl::DEL_HOST_SUBID4_ID, - "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type=? " + "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type = ? " "AND dhcp_identifier = ?"}, {MySqlHostDataSourceImpl::DEL_HOST_SUBID6_ID, - "DELETE FROM hosts WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type=? " + "DELETE FROM hosts WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type = ? " "AND dhcp_identifier = ?"} } @@ -2961,7 +2960,8 @@ MySqlHostDataSource::get6(const SubnetID& subnet_id, // Miscellaneous database methods. -std::string MySqlHostDataSource::getName() const { +std::string +MySqlHostDataSource::getName() const { std::string name = ""; try { name = impl_->conn_.getParameter("name"); @@ -2971,12 +2971,14 @@ std::string MySqlHostDataSource::getName() const { return (name); } -std::string MySqlHostDataSource::getDescription() const { +std::string +MySqlHostDataSource::getDescription() const { return (std::string("Host data source that stores host information" "in MySQL database")); } -std::pair MySqlHostDataSource::getVersion() const { +VersionPair +MySqlHostDataSource::getVersion() const { const MySqlHostDataSourceImpl::StatementIndex stindex = MySqlHostDataSourceImpl::GET_VERSION; @@ -3041,5 +3043,19 @@ MySqlHostDataSource::rollback() { impl_->conn_.rollback(); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +void +MySqlHostDataSource::syncReservations() { + HostCollection hosts = + CfgMgr::instance().getStagingCfg()->getCfgHosts()->getAll(); + for (HostCollection::const_iterator it = hosts.begin(); + it != hosts.end(); ++it) { + try { + add(*it); + } catch(const DuplicateEntry& exception) { + // Don't insert duplicates. + } + } +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/mysql_host_data_source.h b/src/lib/dhcpsrv/mysql_host_data_source.h index d64d3c8d61..e390a9c10d 100644 --- a/src/lib/dhcpsrv/mysql_host_data_source.h +++ b/src/lib/dhcpsrv/mysql_host_data_source.h @@ -298,7 +298,7 @@ class MySqlHostDataSource: public BaseHostDataSource { /// /// @throw isc::dhcp::DbOperationError An operation on the open database /// has failed. - virtual std::pair getVersion() const; + virtual VersionPair getVersion() const; /// @brief Commit Transactions /// @@ -310,6 +310,12 @@ class MySqlHostDataSource: public BaseHostDataSource { /// Rolls back all pending database operations. virtual void rollback(); + /// @brief Sync database tables with kea.conf + /// + /// Truncates database tables, retrieves reservations read from kea.conf and + /// inserts coresponding hosts, reservations and options in the database. + virtual void syncReservations(); + private: /// @brief Pointer to the implementation of the @ref MySqlHostDataSource. MySqlHostDataSourceImpl* impl_; diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 59d105ee42..0cd0cfc3d7 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -516,7 +516,7 @@ class MySqlLease4Exchange : public MySqlLeaseExchange { // bind_[8].is_null = &MLM_FALSE; // commented out for performance // reasons, see memset() above - // state: uint32_t. + // state: uint32_t bind_[9].buffer_type = MYSQL_TYPE_LONG; bind_[9].buffer = reinterpret_cast(&lease_->state_); bind_[9].is_unsigned = MLM_TRUE; @@ -625,7 +625,7 @@ class MySqlLease4Exchange : public MySqlLeaseExchange { // bind_[8].is_null = &MLM_FALSE; // commented out for performance // reasons, see memset() above - // state: uint32_t + // state: uint32_t bind_[9].buffer_type = MYSQL_TYPE_LONG; bind_[9].buffer = reinterpret_cast(&state_); bind_[9].is_unsigned = MLM_TRUE; @@ -985,7 +985,7 @@ class MySqlLease6Exchange : public MySqlLeaseExchange { bind_[14].is_null = &hwaddr_null_; } - // state: uint32_t + // state: uint32_t bind_[15].buffer_type = MYSQL_TYPE_LONG; bind_[15].buffer = reinterpret_cast(&lease_->state_); bind_[15].is_unsigned = MLM_TRUE; @@ -1138,7 +1138,7 @@ class MySqlLease6Exchange : public MySqlLeaseExchange { bind_[14].buffer = reinterpret_cast(&hwaddr_source_); bind_[14].is_unsigned = MLM_TRUE; - // state: uint32_t + // state: uint32_t bind_[15].buffer_type = MYSQL_TYPE_LONG; bind_[15].buffer = reinterpret_cast(&state_); bind_[15].is_unsigned = MLM_TRUE; @@ -1301,7 +1301,7 @@ class MySqlLeaseStatsQuery : public LeaseStatsQuery { // Set the number of columns in the bind array based on fetch_type // This is the number of columns expected in the result set bind_(fetch_type_ ? 4 : 3), - subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) { + subnet_id_(0), lease_type_(0), state_(0), state_count_(0) { validateStatement(); } @@ -1321,7 +1321,7 @@ class MySqlLeaseStatsQuery : public LeaseStatsQuery { // Set the number of columns in the bind array based on fetch_type // This is the number of columns expected in the result set bind_(fetch_type_ ? 4 : 3), - subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) { + subnet_id_(0), lease_type_(0), state_(0), state_count_(0) { validateStatement(); } @@ -1345,7 +1345,7 @@ class MySqlLeaseStatsQuery : public LeaseStatsQuery { // Set the number of columns in the bind array based on fetch_type // This is the number of columns expected in the result set bind_(fetch_type_ ? 4 : 3), - subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) { + subnet_id_(0), lease_type_(0), state_(0), state_count_(0) { validateStatement(); } @@ -1404,7 +1404,7 @@ class MySqlLeaseStatsQuery : public LeaseStatsQuery { // state: uint32_t bind_[col].buffer_type = MYSQL_TYPE_LONG; - bind_[col].buffer = reinterpret_cast(&lease_state_); + bind_[col].buffer = reinterpret_cast(&state_); bind_[col].is_unsigned = MLM_TRUE; ++col; @@ -1445,7 +1445,7 @@ class MySqlLeaseStatsQuery : public LeaseStatsQuery { if (status == MLM_MYSQL_FETCH_SUCCESS) { row.subnet_id_ = static_cast(subnet_id_); row.lease_type_ = static_cast(lease_type_); - row.lease_state_ = lease_state_; + row.lease_state_ = state_; row.state_count_ = state_count_; have_row = true; } else if (status != MYSQL_NO_DATA) { @@ -1488,7 +1488,7 @@ class MySqlLeaseStatsQuery : public LeaseStatsQuery { /// @brief Receives the lease type when fetching a row uint32_t lease_type_; /// @brief Receives the lease state when fetching a row - uint32_t lease_state_; + uint32_t state_; /// @brief Receives the state count when fetching a row int64_t state_count_; }; @@ -2370,7 +2370,7 @@ MySqlLeaseMgr::getDescription() const { return (std::string("MySQL Database")); } -std::pair +VersionPair MySqlLeaseMgr::getVersion() const { const StatementIndex stindex = GET_VERSION; @@ -2410,6 +2410,16 @@ MySqlLeaseMgr::getVersion() const { return (std::make_pair(major, minor)); } +bool +MySqlLeaseMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_BEGIN_TRANSACTION); + if (mysql_query(conn_.mysql_, "START TRANSACTION")) { + isc_throw(DbOperationError, "start transaction failed: " << mysql_error(conn_.mysql_)); + } + + return true; +} + void MySqlLeaseMgr::commit() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT); diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index fb2a427659..4e3105ebe1 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -477,7 +477,13 @@ class MySqlLeaseMgr : public LeaseMgr { /// /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - virtual std::pair getVersion() const; + virtual VersionPair getVersion() const; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction(); /// @brief Commit Transactions /// diff --git a/src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc new file mode 100644 index 0000000000..f3046f79d1 --- /dev/null +++ b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc @@ -0,0 +1,981 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace { + +using namespace isc::dhcp; + +constexpr size_t INSTANCE_ID_MAX_LEN = 128; +constexpr size_t SERVER_CONFIG_MAX_LEN = 65535; +constexpr size_t CONFIG_DATABASE_MAX_LEN = 65535; +constexpr size_t CONFIG_DATABASE_NAME_MAX_LEN = 128; + +std::array tagged_statements = {{ + {MySqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG4, + "INSERT INTO server_configuration4" + " (instance_id, timestamp, server_config, config_database, config_database_name)" + " VALUES (?, ?, ?, ?, ?)"}, + + {MySqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG6, + "INSERT INTO server_configuration6" + " (instance_id, timestamp, server_config, config_database, config_database_name)" + " VALUES (?, ?, ?, ?, ?)"}, + + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_BY_SRV_ID, + "SELECT instance_id, timestamp, server_config, config_database, config_database_name" + " FROM server_configuration4" + " WHERE instance_id = ? "}, + + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_BY_SRV_ID, + "SELECT instance_id, timestamp, server_config, config_database, config_database_name" + " FROM server_configuration6" + " WHERE instance_id = ? "}, + + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_BY_SHARD_DB, + "SELECT instance_id, timestamp, server_config, config_database, config_database_name" + " FROM server_configuration4" + " WHERE config_database_name = ?"}, + + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_BY_SHARD_DB, + "SELECT instance_id, timestamp, server_config, config_database, config_database_name" + " FROM server_configuration6" + " WHERE config_database_name = ?"}, + + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_TIMESTAMP, + "SELECT instance_id, timestamp" + " FROM server_configuration4" + " WHERE instance_id = ? "}, + + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_TIMESTAMP, + "SELECT instance_id, timestamp" + " FROM server_configuration6" + " WHERE instance_id = ? "}, + + {MySqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG4, + "TRUNCATE server_configuration4 "}, + + {MySqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG6, + "TRUNCATE server_configuration6 "}, + + {MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG4_SHARDS_NAME, + "SELECT config_database_name" + " FROM server_configuration4"}, + + {MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG6_SHARDS_NAME, + "SELECT config_database_name" + " FROM server_configuration6"}, + + {MySqlSrvConfigMasterMgr::GET_VERSION, + "SELECT version, minor" + " FROM master_schema_version"}, +}}; + +} // namespace + +namespace isc { +namespace dhcp { + +/// @brief Exchange MySQL and SrvConfigMasterInfo data +/// +/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to +/// describe the parameters in the prepared statements. Where information is +/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that +/// structure is identical. This class handles the creation of that array. +/// +/// Owing to the MySQL API, the process requires some intermediate variables +/// to hold things like data length etc. This object holds those variables. +/// +/// @note There are no unit tests for this class. It is tested indirectly +/// in all MySqlSrvConfigMasterMgr::xxx() calls where it is used. +class MySqlMasterConfigExchange { + /// @brief Set number of database columns to send data for + /// the server configuration structure + static const size_t SERVER_CONFIG_SEND_COLUMNS = 5; + + /// @brief Set number of database columns to read data for + /// the server configuration structure + static const size_t SERVER_CONFIG_RECEIVE_COLUMNS = 5; + +public: + MySqlMasterConfigExchange() : config_master_(boost::make_shared()) { + size_t i = 0; + for (std::string const& column : {"instance_id", "timestamp", "server_config", + "config_database", "config_database_name"}) { + send_columns_[i] = column; + receive_columns_[i] = column; + BOOST_ASSERT(i < SERVER_CONFIG_SEND_COLUMNS); + BOOST_ASSERT(i < SERVER_CONFIG_RECEIVE_COLUMNS); + ++i; + } + + std::fill(&error_receive[0], &error_receive[SERVER_CONFIG_RECEIVE_COLUMNS], MLM_FALSE); + std::fill(&error_send[0], &error_send[SERVER_CONFIG_SEND_COLUMNS], MLM_FALSE); + } + + virtual ~MySqlMasterConfigExchange() { + } + + /// @brief Create MYSQL_BIND objects for SrvConfigMasterInfo Pointer + /// + /// Fills in the MYSQL_BIND array for sending data in the + /// SrvConfigMasterInfo object to the database. + /// + /// @param instance_id the instance identifier of the server being added to the database + /// @param server_config configuration of the server being added to the database + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return Vector of MySQL BIND objects representing the data to be added. + std::vector createBindForSend(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) { + // Store configuration object to ensure it remains valid. + config_master_->instance_id_ = instance_id; + config_master_->timestamp_ = static_cast(time(NULL)); + config_master_->server_config_ = server_config; + config_master_->config_database_ = config_database; + config_master_->config_database_name_ = config_database_name; + + // Initialize prior to constructing the array of MYSQL_BIND structures. + // It sets all fields, including is_null, to zero, so we need to set + // is_null only if it should be true. This gives up minor performance + // benefit while being safe approach. For improved readability, the + // code that explicitly sets is_null is there, but is commented out. + memset(bind_send, 0, sizeof(bind_send)); + + // Set up the structures for the various components of the + // server configuration structure. + try { + // instance_id: VARCHAR(128) + bind_send[0].buffer_type = MYSQL_TYPE_STRING; + bind_send[0].buffer = const_cast(config_master_->instance_id_.c_str()); + bind_send[0].buffer_length = config_master_->instance_id_.length(); + // bind_send[0].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // timestamp: TIMESTAMP + MySqlConnection::convertToDatabaseTime(config_master_->timestamp_, timestamp_buffer_); + bind_send[1].buffer_type = MYSQL_TYPE_TIMESTAMP; + bind_send[1].buffer = reinterpret_cast(×tamp_buffer_); + bind_send[1].buffer_length = sizeof(timestamp_buffer_); + // bind_send[1].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // server_config: TEXT + bind_send[2].buffer_type = MYSQL_TYPE_STRING; + bind_send[2].buffer = const_cast(config_master_->server_config_.c_str()); + bind_send[2].buffer_length = config_master_->server_config_.length(); + // bind_send[2].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // config_database: TEXT + bind_send[3].buffer_type = MYSQL_TYPE_STRING; + bind_send[3].buffer = const_cast(config_master_->config_database_.c_str()); + bind_send[3].buffer_length = config_master_->config_database_.length(); + // bind_send[3].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // config_database_name: VARCHAR(128) + bind_send[4].buffer_type = MYSQL_TYPE_STRING; + bind_send[4].buffer = const_cast(config_master_->config_database_name_.c_str()); + bind_send[4].buffer_length = config_master_->config_database_name_.length(); + // bind_send[4].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // Add the error flags + setErrorIndicators(bind_send, error_send, SERVER_CONFIG_SEND_COLUMNS); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not create bind array from master configuration." + << "Reason: " << ex.what()); + } + + // Add the data to the vector. Note the end element is one after the + // end of the array. + return (std::vector(&bind_send[0], &bind_send[SERVER_CONFIG_SEND_COLUMNS])); + } + + /// @brief Create BIND array to receive data + /// + /// Creates a MYSQL_BIND array to receive SrvConfigMasterInfo data + /// from the database. + /// After data is successfully received, getServerConfigurationData() + /// can be used to copy it to a Lease6 object. + /// + std::vector createBindForReceive() { + // Initialize MYSQL_BIND array. + // It sets all fields, including is_null, to zero, so we need to set + // is_null only if it should be true. This gives up minor performance + // benefit while being safe approach. For improved readability, the + // code that explicitly sets is_null is there, but is commented out. + memset(bind_receive, 0, sizeof(bind_receive)); + + // instance_id: VARCHAR(128) + instance_id_length_ = sizeof(instance_id_buffer_); + bind_receive[0].buffer_type = MYSQL_TYPE_STRING; + bind_receive[0].buffer = reinterpret_cast(instance_id_buffer_); + bind_receive[0].buffer_length = instance_id_length_; + bind_receive[0].length = &instance_id_length_; + // bind_receive[0].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // expire: timestamp + bind_receive[1].buffer_type = MYSQL_TYPE_TIMESTAMP; + bind_receive[1].buffer = reinterpret_cast(×tamp_); + bind_receive[1].buffer_length = sizeof(timestamp_); + // bind_receive[1].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // server_config: TEXT + server_config_length_ = sizeof(server_config_buffer_); + bind_receive[2].buffer_type = MYSQL_TYPE_STRING; + bind_receive[2].buffer = reinterpret_cast(server_config_buffer_); + bind_receive[2].buffer_length = server_config_length_; + bind_receive[2].length = &server_config_length_; + // bind_receive[2].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // config_database: TEXT + config_database_length_ = sizeof(config_database_buffer_); + bind_receive[3].buffer_type = MYSQL_TYPE_STRING; + bind_receive[3].buffer = reinterpret_cast(config_database_buffer_); + bind_receive[3].buffer_length = config_database_length_; + bind_receive[3].length = &config_database_length_; + // bind_receive[3].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // config_database_name: VARCHAR(128) + config_database_name_length_ = sizeof(config_database_name_buffer_); + bind_receive[4].buffer_type = MYSQL_TYPE_STRING; + bind_receive[4].buffer = reinterpret_cast(config_database_name_buffer_); + bind_receive[4].buffer_length = config_database_name_length_; + bind_receive[4].length = &config_database_name_length_; + // bind_receive[4].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // Add the error flags + setErrorIndicators(bind_receive, error_receive, SERVER_CONFIG_RECEIVE_COLUMNS); + + // .. and check that we have the numbers correct at compile time. + BOOST_STATIC_ASSERT(4 < SERVER_CONFIG_RECEIVE_COLUMNS); + + // Add the data to the vector. Note the end element is one after the + // end of the array. + return (std::vector(&bind_receive[0], + &bind_receive[SERVER_CONFIG_RECEIVE_COLUMNS])); + } + + /// @brief Copy Received Data into SrvConfigMasterInfo Object + /// + /// Called after the MYSQL_BIND array created by createBindForReceive() + /// has been used, this copies data from the internal member variables + /// into a SrvConfigMasterInfo object. + /// + /// @return SrvConfigMasterInfoPtr Pointer to a SrvConfigMasterInfo + /// object holding the relevant data. + SrvConfigMasterInfoPtr getServerConfigData() { + SrvConfigMasterInfoPtr config_ptr = boost::make_shared(); + config_ptr->instance_id_ = std::string(instance_id_buffer_, instance_id_length_); + config_ptr->timestamp_ = timestamp_; + config_ptr->server_config_ = std::string(server_config_buffer_, server_config_length_); + config_ptr->config_database_ = + std::string(config_database_buffer_, config_database_length_); + config_ptr->config_database_name_ = + std::string(config_database_name_buffer_, config_database_name_length_); + + return (config_ptr); + } + + /// @brief Return columns in error + /// + /// If an error is returned from a fetch (in particular, a truncated + /// status), this method can be called to get the names of the fields in + /// error. It returns a string comprising the names of the fields + /// separated by commas. In the case of there being no error indicators + /// set, it returns the string "(None)". + /// + /// @return Comma-separated list of columns in error, or the string + /// "(None)". + std::string getErrorSendColumns() { + return (getColumnsInError(error_send, send_columns_, SERVER_CONFIG_SEND_COLUMNS)); + } + + std::string getErrorReceiveColumns() { + return (getColumnsInError(error_receive, receive_columns_, SERVER_CONFIG_RECEIVE_COLUMNS)); + } + + /// @brief Return columns in error + /// + /// If an error is returned from a fetch (in particular, a truncated + /// status), this method can be called to get the names of the fields in + /// error. It returns a string comprising the names of the fields + /// separated by commas. In the case of there being no error indicators + /// set, it returns the string "(None)". + /// + /// @param error Array of error elements. An element is set to MLM_TRUE + /// if the corresponding column in the database is the source of + /// the error. + /// @param names Array of column names, the same size as the error array. + /// @param count Size of each of the arrays. + static std::string getColumnsInError(my_bool* error, std::string* names, size_t count) { + std::string result = ""; + + // Accumulate list of column names + for (size_t i = 0; i < count; ++i) { + if (error[i] == MLM_TRUE) { + if (!result.empty()) { + result += ", "; + } + result += names[i]; + } + } + + if (result.empty()) { + result = "(None)"; + } + + return (result); + } + +protected: + /// @brief Set error indicators + /// + /// Sets the error indicator for each of the MYSQL_BIND elements. It points + /// the "error" field within an element of the input array to the + /// corresponding element of the passed error array. + /// + /// @param bind Array of BIND elements + /// @param error Array of error elements. If there is an error in getting + /// data associated with one of the "bind" elements, the + /// corresponding element in the error array is set to MLM_TRUE. + /// @param count Size of each of the arrays. + static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error, size_t count) { + for (size_t i = 0; i < count; ++i) { + error[i] = MLM_FALSE; + bind[i].error = reinterpret_cast(&error[i]); + } + } + +private: + // Bind arays + MYSQL_BIND bind_send[SERVER_CONFIG_SEND_COLUMNS]; + MYSQL_BIND bind_receive[SERVER_CONFIG_RECEIVE_COLUMNS]; + + // Send column names + std::string send_columns_[SERVER_CONFIG_SEND_COLUMNS]; + // Receive column names + std::string receive_columns_[SERVER_CONFIG_RECEIVE_COLUMNS]; + + // Error indicators + my_bool error_send[SERVER_CONFIG_SEND_COLUMNS]; + my_bool error_receive[SERVER_CONFIG_RECEIVE_COLUMNS]; + + // Pointer to the master configuration object + SrvConfigMasterInfoPtr config_master_; + + // Instance identifier buffer and length + char instance_id_buffer_[INSTANCE_ID_MAX_LEN]; + unsigned long instance_id_length_; ///< Instance identifier length + + // Timestamp buffer and length + MYSQL_TIME timestamp_buffer_; + int64_t timestamp_; + + // Server configuration buffer and length + char server_config_buffer_[SERVER_CONFIG_MAX_LEN]; + unsigned long server_config_length_; + + // Configuration buffer and length + char config_database_buffer_[CONFIG_DATABASE_MAX_LEN]; + unsigned long config_database_length_; + + // Database name buffer and length + char config_database_name_buffer_[CONFIG_DATABASE_NAME_MAX_LEN]; + unsigned long config_database_name_length_; +}; + +MySqlSrvConfigMasterMgr::MySqlSrvConfigMasterMgr(const MySqlConnection::ParameterMap& parameters) + : conn_(parameters), exchange_(new MySqlMasterConfigExchange()) { + + // Open the database. + conn_.openDatabase(); + + // Enable autocommit. To avoid a flush to disk on every commit, the global + // parameter innodb_flush_log_at_trx_commit should be set to 2. This will + // cause the changes to be written to the log, but flushed to disk in the + // background every second. Setting the parameter to that value will speed + // up the system, but at the risk of losing data if the system crashes. + my_bool result = mysql_autocommit(conn_.mysql_, 1); + if (result != 0) { + isc_throw(DbOperationError, mysql_error(conn_.mysql_)); + } + + // Prepare all statements likely to be used. + conn_.prepareStatements(tagged_statements.begin(), tagged_statements.end()); +} + +MySqlSrvConfigMasterMgr::~MySqlSrvConfigMasterMgr() { + // There is no need to close the database in this destructor: it is + // closed in the destructor of the mysql_ member variable. +} + +std::string MySqlSrvConfigMasterMgr::getDBVersion() { + std::stringstream tmp; + tmp << "MySQL backend " << MYSQL_SCHEMA_VERSION_MAJOR; + tmp << "." << MYSQL_SCHEMA_VERSION_MINOR; + tmp << ", library " << mysql_get_client_info(); + return (tmp.str()); +} + +VersionPair MySqlSrvConfigMasterMgr::getVersion() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_VERSION); + + const StatementIndex stindex = GET_VERSION; + + uint32_t major; // Major version number + uint32_t minor; // Minor version number + + // Execute the prepared statement + int status = mysql_stmt_execute(conn_.statements_[stindex]); + if (status != 0) { + isc_throw(DbOperationError, "unable to execute <" + << conn_.text_statements_[stindex] + << "> - reason: " << mysql_error(conn_.mysql_)); + } + + // Bind the output of the statement to the appropriate variables. + MYSQL_BIND bind[2]; + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].is_unsigned = 1; + bind[0].buffer = &major; + bind[0].buffer_length = sizeof(major); + + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].is_unsigned = 1; + bind[1].buffer = &minor; + bind[1].buffer_length = sizeof(minor); + + status = mysql_stmt_bind_result(conn_.statements_[stindex], bind); + if (status != 0) { + isc_throw(DbOperationError, "unable to bind result set: " << mysql_error(conn_.mysql_)); + } + + // Fetch the data and set up the "release" object to release associated + // resources when this method exits then retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[stindex]); + status = mysql_stmt_fetch(conn_.statements_[stindex]); + if (status != 0) { + isc_throw(DbOperationError, "unable to obtain result set: " << mysql_error(conn_.mysql_)); + } + + return (std::make_pair(major, minor)); +} + +bool MySqlSrvConfigMasterMgr::addServerConfig4(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG4) + .arg(instance_id) + .arg(config_database_name); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG4; + return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, + config_database_name); +} + +bool MySqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG6) + .arg(instance_id) + .arg(config_database_name); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG6; + return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, + config_database_name); +} + +bool MySqlSrvConfigMasterMgr::clearServersConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG4); + + const StatementIndex stindex = DELETE_SERVER_CONFIG4; + + // Execute the prepared statement + int status = mysql_stmt_execute(conn_.statements_[stindex]); + checkError(status, stindex, "unable to execute"); + + return true; +} + +bool MySqlSrvConfigMasterMgr::clearServersConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG6); + + const StatementIndex stindex = DELETE_SERVER_CONFIG6; + + // Execute the prepared statement + int status = mysql_stmt_execute(conn_.statements_[stindex]); + checkError(status, stindex, "unable to execute"); + + return true; +} + +SrvConfigMasterInfoPtr MySqlSrvConfigMasterMgr::getConfig4(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4) + .arg(instance_id); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_BY_SRV_ID; + return getCommonServerConfigurationByInstanceId(statement_index, instance_id); +} + +SrvConfigMasterInfoPtr MySqlSrvConfigMasterMgr::getConfig6(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6) + .arg(instance_id); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_BY_SRV_ID; + return getCommonServerConfigurationByInstanceId(statement_index, instance_id); +} + +bool MySqlSrvConfigMasterMgr::getConfig4( + const std::string& config_database_name, + std::vector& server_configurations) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB) + .arg(config_database_name); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_BY_SHARD_DB; + return getCommonServerConfigurationByShardName(statement_index, config_database_name, + server_configurations); +} + +bool MySqlSrvConfigMasterMgr::getConfig6( + const std::string& config_database_name, + std::vector& server_configurations) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB) + .arg(config_database_name); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_BY_SHARD_DB; + return getCommonServerConfigurationByShardName(statement_index, config_database_name, + server_configurations); +} + +SrvConfigMasterInfoPtr +MySqlSrvConfigMasterMgr::getMasterConfig4Timestamp(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP) + .arg(instance_id); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_TIMESTAMP; + return getCommonTimestampFromMasterServerConfiguration(statement_index, instance_id); +} + +SrvConfigMasterInfoPtr +MySqlSrvConfigMasterMgr::getMasterConfig6Timestamp(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP) + .arg(instance_id); + ; + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_TIMESTAMP; + return getCommonTimestampFromMasterServerConfiguration(statement_index, instance_id); +} + +bool MySqlSrvConfigMasterMgr::getServersConfig4ShardsName( + std::set& shards_list) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG4_SHARDS_NAME; + return getCommonShardNamesFromMasterServerConfiguration(statement_index, shards_list); +} + +bool MySqlSrvConfigMasterMgr::getServersConfig6ShardsName( + std::set& shards_list) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME); + + const StatementIndex statement_index = MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG6_SHARDS_NAME; + return getCommonShardNamesFromMasterServerConfiguration(statement_index, shards_list); +} + +bool MySqlSrvConfigMasterMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_BEGIN_TRANSACTION); + if (mysql_query(conn_.mysql_, "START TRANSACTION")) { + isc_throw(DbOperationError, "start transaction failed: " << mysql_error(conn_.mysql_)); + } + + return true; +} + +void MySqlSrvConfigMasterMgr::commit() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT); + if (mysql_commit(conn_.mysql_) != 0) { + isc_throw(DbOperationError, "commit failed: " << mysql_error(conn_.mysql_)); + } +} + +void MySqlSrvConfigMasterMgr::rollback() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK); + if (mysql_rollback(conn_.mysql_) != 0) { + isc_throw(DbOperationError, "rollback failed: " << mysql_error(conn_.mysql_)); + } +} + +void MySqlSrvConfigMasterMgr::checkError(int status, + StatementIndex statement_index, + const char* what) const { + conn_.checkError(status, statement_index, what); +} + +bool MySqlSrvConfigMasterMgr::addCommonServerConfiguration( + const StatementIndex statement_index, + const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + + std::vector bind = exchange_->createBindForSend( + instance_id, server_config, config_database, config_database_name); + try { + int status = mysql_stmt_bind_param(conn_.statements_[statement_index], &bind[0]); + checkError(status, statement_index, "unable to bind parameters"); + + // Execute the statement + status = mysql_stmt_execute(conn_.statements_[statement_index]); + if (status != 0) { + // Failure: check for the special case of duplicate entry. If this is + // the case, we return false to indicate that the row was not added. + // Otherwise we throw an exception. + if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) { + return (false); + } + checkError(status, statement_index, "unable to execute"); + } + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not create bind array to insert configuration " + << ", reason: " << ex.what()); + } + + // Insert succeeded + return true; +} + +SrvConfigMasterInfoPtr MySqlSrvConfigMasterMgr::getCommonServerConfigurationByInstanceId( + const StatementIndex statement_index, const std::string& instance_id) const { + + // Set up the WHERE clause value + MYSQL_BIND inbind[1]; + memset(inbind, 0, sizeof(inbind)); + + inbind[0].buffer_type = MYSQL_TYPE_STRING; + inbind[0].buffer = const_cast(instance_id.c_str()); + inbind[0].buffer_length = instance_id.length(); + inbind[0].is_null = 0; + + SrvConfigMasterInfoPtr result; + + // Bind the selection parameters to the statement + int status = mysql_stmt_bind_param(conn_.statements_[statement_index], inbind); + checkError(status, statement_index, "unable to bind WHERE clause parameter"); + + // Set up the MYSQL_BIND array for the data being returned and bind it to + // the statement. + std::vector outbind = exchange_->createBindForReceive(); + status = mysql_stmt_bind_result(conn_.statements_[statement_index], &outbind[0]); + checkError(status, statement_index, "unable to bind SELECT clause parameters"); + + // Execute the statement + status = mysql_stmt_execute(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to execute"); + + // Ensure that all the lease information is retrieved in one go to avoid + // overhead of going back and forth between client and server. + status = mysql_stmt_store_result(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to set up for storing all results"); + + // Set up the fetch "release" object to release resources associated + // with the call to mysql_stmt_fetch when this method exits, then + // retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[statement_index]); + int count = 0; + while ((status = mysql_stmt_fetch(conn_.statements_[statement_index])) == 0) { + try { + result = exchange_->getServerConfigData(); + } catch (const isc::BadValue& ex) { + // Rethrow the exception with a bit more data. + isc_throw(BadValue, ex.what() << ". Statement is <" + << conn_.text_statements_[statement_index] << ">"); + } + + if (++count > 1) { + isc_throw(MultipleRecords, "multiple records were found in the " + "database where only one was expected for query " + << conn_.text_statements_[statement_index]); + } + } + + // How did the fetch end? + if (status == 1) { + // Error - unable to fetch results + checkError(status, statement_index, "unable to fetch results"); + } else if (status == MYSQL_DATA_TRUNCATED) { + // Data truncated - throw an exception indicating what was at fault + isc_throw(DataTruncated, conn_.text_statements_[statement_index] + << " returned truncated data: columns affected are " + << exchange_->getErrorReceiveColumns()); + } + + return result; +} + +bool MySqlSrvConfigMasterMgr::getCommonServerConfigurationByShardName( + const StatementIndex statement_index, + const std::string& config_database_name, + std::vector& server_configurations) const { + + server_configurations.clear(); + + // Set up the WHERE clause value + MYSQL_BIND inbind[1]; + memset(inbind, 0, sizeof(inbind)); + + inbind[0].buffer_type = MYSQL_TYPE_STRING; + inbind[0].buffer = const_cast(config_database_name.c_str()); + inbind[0].buffer_length = config_database_name.length(); + inbind[0].is_null = 0; + + // Bind the selection parameters to the statement + int status = mysql_stmt_bind_param(conn_.statements_[statement_index], inbind); + checkError(status, statement_index, "unable to bind WHERE clause parameter"); + + // Set up the MYSQL_BIND array for the data being returned and bind it to + // the statement. + std::vector outbind = exchange_->createBindForReceive(); + status = mysql_stmt_bind_result(conn_.statements_[statement_index], &outbind[0]); + checkError(status, statement_index, "unable to bind SELECT clause parameters"); + + // Execute the statement + status = mysql_stmt_execute(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to execute"); + + // Ensure that all the lease information is retrieved in one go to avoid + // overhead of going back and forth between client and server. + status = mysql_stmt_store_result(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to set up for storing all results"); + + // Set up the fetch "release" object to release resources associated + // with the call to mysql_stmt_fetch when this method exits, then + // retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[statement_index]); + + while ((status = mysql_stmt_fetch(conn_.statements_[statement_index])) == 0) { + try { + server_configurations.push_back(exchange_->getServerConfigData()); + } catch (const isc::BadValue& ex) { + // Rethrow the exception with a bit more data. + isc_throw(BadValue, ex.what() << ". Statement is <" + << conn_.text_statements_[statement_index] << ">"); + } + } + + // How did the fetch end? + if (status == 1) { + // Error - unable to fetch results + checkError(status, statement_index, "unable to fetch results"); + } else if (status == MYSQL_DATA_TRUNCATED) { + // Data truncated - throw an exception indicating what was at fault + isc_throw(DataTruncated, conn_.text_statements_[statement_index] + << " returned truncated data: columns affected are " + << exchange_->getErrorReceiveColumns()); + } + + return true; +} + +SrvConfigMasterInfoPtr MySqlSrvConfigMasterMgr::getCommonTimestampFromMasterServerConfiguration( + const StatementIndex statement_index, const std::string& instance_id) const { + + // Set up the WHERE clause value + MYSQL_BIND inbind[1]; + memset(inbind, 0, sizeof(inbind)); + + inbind[0].buffer_type = MYSQL_TYPE_STRING; + inbind[0].buffer = const_cast(instance_id.c_str()); + inbind[0].buffer_length = instance_id.length(); + inbind[0].is_null = 0; + + // Bind the selection parameters to the statement + int status = mysql_stmt_bind_param(conn_.statements_[statement_index], inbind); + checkError(status, statement_index, "unable to bind WHERE clause parameter"); + + MYSQL_BIND outbind[2]; + memset(outbind, 0, sizeof(outbind)); + + char instance_id_buffer[INSTANCE_ID_MAX_LEN]; + unsigned long instance_id_length = sizeof(instance_id_buffer); + + // Server Id: VARCHAR(128) + outbind[0].buffer_type = MYSQL_TYPE_STRING; + outbind[0].buffer = reinterpret_cast(instance_id_buffer); + outbind[0].buffer_length = instance_id_length; + outbind[0].length = &instance_id_length; + // outbind[0].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + // Timestamp + int64_t timestamp = 0; + outbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP; + outbind[1].buffer = reinterpret_cast(×tamp); + outbind[1].is_unsigned = MLM_TRUE; + outbind[1].buffer_length = instance_id_length; + // outbind[1].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + status = mysql_stmt_bind_result(conn_.statements_[statement_index], &outbind[0]); + checkError(status, statement_index, "unable to bind SELECT clause parameters"); + + // Execute the statement + status = mysql_stmt_execute(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to execute"); + + // Ensure that all the information is retrieved in one go to avoid + // overhead of going back and forth between client and server. + status = mysql_stmt_store_result(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to set up for storing all results"); + + // Set up the fetch "release" object to release resources associated + // with the call to mysql_stmt_fetch when this method exits, then + // retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[statement_index]); + + int count = 0; + SrvConfigMasterInfoPtr result; + while ((status = mysql_stmt_fetch(conn_.statements_[statement_index])) == 0) { + try { + std::string instance_id(instance_id_buffer, instance_id_length); + result.reset(new SrvConfigMasterInfo()); + result->instance_id_ = instance_id; + result->timestamp_ = timestamp; + + if (++count > 1) { + isc_throw(MultipleRecords, "multiple records were found in the " + "database where only one was expected for query " + << conn_.text_statements_[statement_index]); + } + } catch (const isc::BadValue& ex) { + // Rethrow the exception with a bit more data. + isc_throw(BadValue, ex.what() << ". Statement is <" + << conn_.text_statements_[statement_index] << ">"); + } + } + + // How did the fetch end? + if (status == 1) { + // Error - unable to fetch results + checkError(status, statement_index, "unable to fetch results"); + } else if (status == MYSQL_DATA_TRUNCATED) { + // Data truncated - throw an exception indicating what was at fault + isc_throw(DataTruncated, + conn_.text_statements_[statement_index] << " returned truncated data."); + } + + return result; +} + +bool MySqlSrvConfigMasterMgr::getCommonShardNamesFromMasterServerConfiguration( + const StatementIndex statement_index, std::set& shards) const { + shards.clear(); + + MYSQL_BIND outbind[1]; + memset(outbind, 0, sizeof(outbind)); + + char config_database_name_buffer[CONFIG_DATABASE_NAME_MAX_LEN]; + unsigned long config_database_name_length; + + // config_database_name: VARCHAR(128) + config_database_name_length = sizeof(config_database_name_buffer); + outbind[0].buffer_type = MYSQL_TYPE_STRING; + outbind[0].buffer = reinterpret_cast(config_database_name_buffer); + outbind[0].buffer_length = config_database_name_length; + outbind[0].length = &config_database_name_length; + // outbind[0].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above + + int status = mysql_stmt_bind_result(conn_.statements_[statement_index], &outbind[0]); + checkError(status, statement_index, "unable to bind SELECT clause parameters"); + + // Execute the statement + status = mysql_stmt_execute(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to execute"); + + // Ensure that all the information is retrieved in one go to avoid + // overhead of going back and forth between client and server. + status = mysql_stmt_store_result(conn_.statements_[statement_index]); + checkError(status, statement_index, "unable to set up for storing all results"); + + // Set up the fetch "release" object to release resources associated + // with the call to mysql_stmt_fetch when this method exits, then + // retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[statement_index]); + + while ((status = mysql_stmt_fetch(conn_.statements_[statement_index])) == 0) { + try { + std::string config_database(config_database_name_buffer, config_database_name_length); + shards.insert(config_database); + } catch (const isc::BadValue& ex) { + // Rethrow the exception with a bit more data. + isc_throw(BadValue, ex.what() << ". Statement is <" + << conn_.text_statements_[statement_index] << ">"); + } + } + + // How did the fetch end? + if (status == 1) { + // Error - unable to fetch results + checkError(status, statement_index, "unable to fetch results"); + } else if (status == MYSQL_DATA_TRUNCATED) { + // Data truncated - throw an exception indicating what was at fault + + isc_throw(DataTruncated, + conn_.text_statements_[statement_index] << " returned truncated data."); + } + + return true; +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/mysql_srv_config_master_mgr.h b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.h new file mode 100644 index 0000000000..1c11b34136 --- /dev/null +++ b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.h @@ -0,0 +1,310 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MYSQL_CONFIGURATION_MASTER_MGR_H +#define MYSQL_CONFIGURATION_MASTER_MGR_H + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Forward declaration to the exchange. +class MySqlMasterConfigExchange; + +/// @brief Exchange used to interact with the master server configuration table +/// from the database +class MySqlSrvConfigMasterMgr : public SrvConfigMasterMgr { +public: + /// @brief Statement Tags + /// + /// The contents of the enum are indexes into the list of SQL statements + enum StatementIndex { + // Insert DHCPv4 server configuration. + INSERT_SERVER_CONFIG4, + // Insert DHCPv6 server configuration. + INSERT_SERVER_CONFIG6, + // Retrieve DHCPv4 server configuration filtered by server ID. + GET_CONFIGURATION4_BY_SRV_ID, + // Retrieve DHCPv6 server configuration filtered by server ID. + GET_CONFIGURATION6_BY_SRV_ID, + // Retrieve DHCPv4 server configuration filtered by shard name. + GET_CONFIGURATION4_BY_SHARD_DB, + // Retrieve DHCPv6 server configuration filtered by shard name. + GET_CONFIGURATION6_BY_SHARD_DB, + // Retrieve DHCPv4 synchronization timestamp. + GET_CONFIGURATION4_TIMESTAMP, + // Retrieve DHCPv6 synchronization timestamp. + GET_CONFIGURATION6_TIMESTAMP, + // Delete DHCPv4 server configuration. + DELETE_SERVER_CONFIG4, + // Delete DHCPv6 server configuration. + DELETE_SERVER_CONFIG6, + // Retrieve DHCPv4 shard names. + GET_SERVERS_CONFIG4_SHARDS_NAME, + // Retrieve DHCPv6 shard names. + GET_SERVERS_CONFIG6_SHARDS_NAME, + // Retrieve schema version. + GET_VERSION, + // Number of statements + NUM_STATEMENTS + }; + + /// @brief Constructor + /// + /// @param parameters database connection parameters + explicit MySqlSrvConfigMasterMgr(const DatabaseConnection::ParameterMap& parameters); + + /// @brief Destructor + virtual ~MySqlSrvConfigMasterMgr(); + + /// @brief Inserts DHCPv4 server configuration into the master database + /// + /// @param instance_id instance identifier of the server whose configuration is being added + /// @param server_config the configuration being added + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true if server configuration has been added, false otherwise + virtual bool addServerConfig4(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const override; + + /// @brief Inserts DHCPv6 server configuration into the master database + /// + /// @param instance_id instance identifier of the server whose configuration is being added + /// @param server_config the configuration being added + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true if server configuration has been added, false otherwise + virtual bool addServerConfig6(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const override; + + /// @brief Clears all DHCPv4 shard configurations. + /// + /// @return true if shard configurations have been successfully deleted, false otherwise + virtual bool clearServersConfig4() const override; + + /// @brief Clears all DHCPv6 shard configurations. + /// + /// @return true if shard configurations have been successfully deleted, false otherwise + virtual bool clearServersConfig6() const override; + + /// @brief Returns DHCPv4 server configuration information from the database + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one Kea server. + /// + /// @param instance_id the instance identifier of the server being retrieved + /// + /// @return smart pointer to the configuration (or NULL if a configuration is not found) + virtual SrvConfigMasterInfoPtr getConfig4(const std::string& instance_id) const override; + + /// @brief Returns DHCPv6 server configuration information from the database + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one Kea server. + /// + /// @param instance_id the instance identifier of the server being retrieved + /// + /// @return smart pointer to the configuration (or NULL if a configuration is not found) + virtual SrvConfigMasterInfoPtr getConfig6(const std::string& instance_id) const override; + + /// @brief Retrieves configurations of DHCPv4 servers belonging to a given + /// shard. + /// + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations a list of server configurations being retrieved + /// + /// @return true if server configuration has been successfully retrieved, false otherwise + virtual bool getConfig4(const std::string& config_database_name, + std::vector& server_configurations) const override; + + /// @brief Retrieves configurations of DHCPv6 servers belonging to a given + /// shard. + /// + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations a list of server configurations being retrieved + /// + /// @return true if server configuration has been successfully retrieved, false otherwise + virtual bool getConfig6(const std::string& config_database_name, + std::vector& server_configurations) const override; + + /// @brief Retrieves timestamp for a DHCPv4 shard server configuration. + /// + /// There is one timestamp for each shard in the master database. They are + /// updated on every write (including updates) to the master database. + /// + /// @param instance_id the instance identifier of the server for which the timestamp is being + /// retrieved + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigMasterInfoPtr + getMasterConfig4Timestamp(const std::string& instance_id) const override; + + /// @brief Retrieves timestamp for a DHCPv6 shard server configuration. + /// + /// There is one timestamp for each shard in the master database. They are + /// updated on every write (including updates) to the master database. + /// + /// @param instance_id the instance identifier of the server for which the timestamp is being + /// retrieved + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigMasterInfoPtr + getMasterConfig6Timestamp(const std::string& instance_id) const override; + + /// @brief Retrieves all shard names containing DHCPv4 servers from the master database. + /// + /// @return true if shard names have been successfully retrieved, false otherwise + virtual bool getServersConfig4ShardsName(std::set& shards) const override; + + /// @brief Retrieves all shard names containing DHCPv6 servers from the master database. + /// + /// @return true if shard names have been successfully retrieved, false otherwise + virtual bool getServersConfig6ShardsName(std::set& shards) const override; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() override; + + /// @brief Commit Transactions + /// + /// Commits all pending database operations. + /// + /// @throw DbOperationError If the commit failed. + virtual void commit() override; + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. + /// + /// @throw DbOperationError If the rollback failed. + virtual void rollback() override; + + /// @brief Retrieves schema version. + /// + /// @return Version number stored in the database, as a pair of unsigned integers. "first" is + /// the major version number, "second" is the minor version number. + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has failed. + VersionPair getVersion() const override; + + /// @brief Local version of getDBVersion() class method + static std::string getDBVersion(); + + /// @brief Returns the type of backend used, in this case "mysql". Useful + /// when called from the base class. + virtual std::string getType() const override { + return (std::string("mysql")); + } + +private: + /// @brief Check Error and Throw Exception + /// + /// This method invokes @ref MySqlConnection::checkError. + /// + /// @param status Status code: non-zero implies an error + /// @param index Index of statement that caused the error + /// @param what High-level description of the error + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + void checkError(int status, StatementIndex statement_index, const char* what) const; + +private: + /// @brief Adds server configuration either for DHCPv4 or for DHCPv6. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param instance_id the instance identifier of the server being added to the database + /// @param server_config configuration of the server being added to the database + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true, if configuration has been successfully added, false otherwise + bool addCommonServerConfiguration(const StatementIndex statement_index, + const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const; + + /// @brief Retrieves a single server configuration either for DHCPv4 or for + /// DHCPv6 filtered by the instance ID of the server. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param instance_id the instance identifier of the server being retrieved from the database + /// + /// @return the retrieved configuration + SrvConfigMasterInfoPtr + getCommonServerConfigurationByInstanceId(const StatementIndex statement_index, + const std::string& instance_id) const; + + /// @brief Retrieves the configurations of the DHCPv4 or DHCPv6 servers + /// belonging to a given shard. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations the retrieved configurations + /// + /// @return true if configurations were found, false otherwise + bool getCommonServerConfigurationByShardName( + const StatementIndex statement_index, + const std::string& config_database_name, + std::vector& server_configurations) const; + + /// @brief Retrieves synchronization timestamps from the master database for + /// a given server. + // + /// @param statement_index index of the prepared statement executed in the database + /// @param instance_id the instance identifier of the server by which timestamps are filtered + /// + /// @return a configuration containing the timestamp in question + SrvConfigMasterInfoPtr + getCommonTimestampFromMasterServerConfiguration(const StatementIndex statement_index, + const std::string& instance_id) const; + + /// @brief Retrieves all the shard names from the master database. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param[out] shards the whole set of shards from the master database + bool getCommonShardNamesFromMasterServerConfiguration(const StatementIndex statement_index, + std::set& shards) const; + + /// @brief Database connection object + mutable MySqlConnection conn_; + // @brief Exchange in charge of data conversion to and from MySQL types + boost::scoped_ptr exchange_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // MYSQL_CONFIGURATION_MASTER_MGR_H diff --git a/src/lib/dhcpsrv/mysql_srv_config_mgr.cc b/src/lib/dhcpsrv/mysql_srv_config_mgr.cc new file mode 100644 index 0000000000..b49c67992a --- /dev/null +++ b/src/lib/dhcpsrv/mysql_srv_config_mgr.cc @@ -0,0 +1,625 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace isc; +using namespace isc::dhcp; + +const size_t CONFIG_DATA_SIZE = 1073741824u; + +namespace { + +std::array tagged_statements = {{ + {MySqlSrvConfigMgr::GET_VERSION, // + "SELECT version, minor" + " FROM config_schema_version"}, + + {MySqlSrvConfigMgr::GET_CONFIGURATION4_TIMESTAMP, // + "SELECT config_id, timestamp" + " FROM server_configuration4 LIMIT 1"}, + + {MySqlSrvConfigMgr::GET_CONFIGURATION6_TIMESTAMP, // + "SELECT config_id, timestamp" + " FROM server_configuration6 LIMIT 1"}, + + {MySqlSrvConfigMgr::GET_JSON_CONFIGURATION4, // + "SELECT config_id, timestamp, json_data" + " FROM server_configuration4 LIMIT 1"}, + + {MySqlSrvConfigMgr::GET_JSON_CONFIGURATION6, // + "SELECT config_id, timestamp, json_data" + " FROM server_configuration6 LIMIT 1"}, + + {MySqlSrvConfigMgr::GET_GENERIC_CONFIGURATION4, // + "SELECT config_id, timestamp, generic_data" + " FROM server_configuration4 LIMIT 1"}, + + {MySqlSrvConfigMgr::GET_GENERIC_CONFIGURATION6, // + "SELECT config_id, timestamp, generic_data" + " FROM server_configuration6 LIMIT 1"}, + + {MySqlSrvConfigMgr::INSERT_CONFIGURATION4, // + "INSERT INTO server_configuration4" + " (config_id, timestamp, json_data, generic_data)" + " VALUES (?, ?, ?, ?)"}, + + {MySqlSrvConfigMgr::INSERT_CONFIGURATION6, // + "INSERT INTO server_configuration6" + " (config_id, timestamp, json_data, generic_data)" + " VALUES (?, ?, ?, ?)"}, + + {MySqlSrvConfigMgr::UPDATE_CONFIGURATION4, // + "UPDATE server_configuration4" + " SET timestamp=?, json_data=?, generic_data=?" + " WHERE config_id=?"}, + + {MySqlSrvConfigMgr::UPDATE_CONFIGURATION6, // + "UPDATE server_configuration6" + " SET timestamp=?, json_data=?, generic_data=?" + " WHERE config_id=?"} // +}}; + +} // namespace + +namespace isc { +namespace dhcp { + +MySqlSrvConfigMgr::MySqlSrvConfigMgr(const MySqlConnection::ParameterMap& parameters) + : conn_(parameters) { + + // Open the database. + conn_.openDatabase(); + + // Enable autocommit. To avoid a flush to disk on every commit, the global + // parameter innodb_flush_log_at_trx_commit should be set to 2. This will + // cause the changes to be written to the log, but flushed to disk in the + // background every second. Setting the parameter to that value will speed + // up the system, but at the risk of losing data if the system crashes. + my_bool result = mysql_autocommit(conn_.mysql_, 1); + if (result != 0) { + isc_throw(DbOperationError, mysql_error(conn_.mysql_)); + } + + // Prepare all statements likely to be used. + conn_.prepareStatements(tagged_statements.begin(), tagged_statements.end()); +} + +MySqlSrvConfigMgr::~MySqlSrvConfigMgr() { + // There is no need to close the database in this destructor: it is + // closed in the destructor of the mysql_ member variable. +} + +std::string MySqlSrvConfigMgr::getDBVersion() { + std::stringstream tmp; + tmp << "MySQL backend " << MYSQL_SCHEMA_VERSION_MAJOR; + tmp << "." << MYSQL_SCHEMA_VERSION_MINOR; + tmp << ", library " << mysql_get_client_info(); + return (tmp.str()); +} + +VersionPair MySqlSrvConfigMgr::getVersion() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_VERSION); + const StatementIndex stindex = GET_VERSION; + + uint32_t major; // Major version number + uint32_t minor; // Minor version number + + // Execute the prepared statement + int status = mysql_stmt_execute(conn_.statements_[stindex]); + if (status != 0) { + isc_throw(DbOperationError, "unable to execute <" + << conn_.text_statements_[stindex] + << "> - reason: " << mysql_error(conn_.mysql_)); + } + + // Bind the output of the statement to the appropriate variables. + MYSQL_BIND bind[2]; + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].is_unsigned = 1; + bind[0].buffer = &major; + bind[0].buffer_length = sizeof(major); + + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].is_unsigned = 1; + bind[1].buffer = &minor; + bind[1].buffer_length = sizeof(minor); + + status = mysql_stmt_bind_result(conn_.statements_[stindex], bind); + if (status != 0) { + isc_throw(DbOperationError, "unable to bind result set: " << mysql_error(conn_.mysql_)); + } + + // Fetch the data and set up the "release" object to release associated + // resources when this method exits then retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[stindex]); + status = mysql_stmt_fetch(conn_.statements_[stindex]); + if (status != 0) { + isc_throw(DbOperationError, "unable to obtain result set: " << mysql_error(conn_.mysql_)); + } + + return (std::make_pair(major, minor)); +} + +bool MySqlSrvConfigMgr::updateConfig4(int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG4); + + // Initiate MYSQL transaction as we will have to update the config + // from two steps and we don't want anybody else to write another + // config between these two steps: + // Step 1: Read the current configuration timestamp from database. + // Step 2: Check if the provided configuration timestamp is + // the same with the existing one from database. + // Step 3: Update the configuration on the server. + // If there is no configuration on the server then insert a new one but + // also using transactions because we don't want another user to insert also + // a new configuration in the same time. + + // MySqlTransaction rolls back the transaction on its destructor. + + // begin the transaction + MySqlTransaction transaction(conn_); + + SrvConfigInfoPtr current_config = getConfig4Timestamp(); + + if (!current_config) { + if (insertConfig4(json_data, generic_data)) { + transaction.commit(); + return true; + } + return false; + } + + if (!updateConfig4(current_config->config_id_, json_data, generic_data)) { + // The configuration timestamp has been changed since the last + // configuration read until this update. + + LOG_WARN(dhcpsrv_logger, DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED) + .arg(config_timestamp) + .arg(current_config->timestamp_); + + return false; + } + + transaction.commit(); + return true; +} + +bool MySqlSrvConfigMgr::updateConfig6(int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG6); + + // Initiate MYSQL transaction as we will have to update the config + // from two steps and we don't want anybody else to write another + // config between these two steps: + // Step 1: Read the current configuration timestamp from database. + // Step 2: Check if the provided configuration timestamp is + // the same with the existing one from database. + // Step 3: Update the configuration on the server. + // If there is no configuration on the server then insert a new one but + // also using transactions because we don't want another user to insert also + // a new configuration in the same time. + + // MySqlTransaction rolls back the transaction on its destructor. + + // begin the transaction + MySqlTransaction transaction(conn_); + + SrvConfigInfoPtr current_config = getConfig6Timestamp(); + + if (!current_config) { + if (insertConfig6(json_data, generic_data)) { + transaction.commit(); + return true; + } + return false; + } + + if (!updateConfig6(current_config->config_id_, json_data, generic_data)) { + // The configuration timestamp has been changed since the last + // configuration read until this update. + + LOG_WARN(dhcpsrv_logger, DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED) + .arg(config_timestamp) + .arg(current_config->timestamp_); + + return false; + } + + transaction.commit(); + return true; +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getConfig4Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_CONFIG4_TIMESTAMP); + + const StatementIndex stindex = MySqlSrvConfigMgr::GET_CONFIGURATION4_TIMESTAMP; + return getConfigTimestampCommon(stindex); +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getConfig6Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_CONFIG6_TIMESTAMP); + + const StatementIndex stindex = MySqlSrvConfigMgr::GET_CONFIGURATION6_TIMESTAMP; + return getConfigTimestampCommon(stindex); +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getConfigTimestampCommon(StatementIndex stindex) const { + char config_id[37]; // SQL type CHAR(36) + NULL character + MYSQL_TIME db_timestamp; + + // Execute the statement + int status = mysql_stmt_execute(conn_.statements_[stindex]); + checkError(status, stindex, "unable to execute"); + + // Bind the output of the statement to the appropriate variables. + MYSQL_BIND bind[2]; + memset(bind, 0, sizeof(bind)); + + unsigned long config_id_length = sizeof(config_id); + bind[0].buffer_type = MYSQL_TYPE_STRING; + bind[0].buffer = &config_id[0]; + bind[0].buffer_length = config_id_length; + bind[0].length = &config_id_length; + + bind[1].buffer_type = MYSQL_TYPE_TIMESTAMP; + bind[1].buffer = reinterpret_cast(&db_timestamp); + bind[1].buffer_length = sizeof(db_timestamp); + + // overhead of going back and forth between client and server. + // status = mysql_stmt_store_result(conn_.statements_[stindex]); + status = mysql_stmt_bind_result(conn_.statements_[stindex], bind); + checkError(status, stindex, "unable to set up for storing all results"); + + // Fetch the data and set up the "release" object to release associated + // resources when this method exits then retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[stindex]); + status = mysql_stmt_fetch(conn_.statements_[stindex]); + if (status == MYSQL_NO_DATA) { + return SrvConfigInfoPtr(); + } + checkError(status, stindex, "unable to obtain result set"); + + time_t timestamp; + MySqlConnection::convertFromDatabaseTime(db_timestamp, 0, timestamp); + + SrvConfigInfoPtr srvConf = boost::make_shared(); + srvConf->config_id_ = std::string(&config_id[0], config_id_length); + srvConf->timestamp_ = timestamp; + + return srvConf; +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getJsonConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_CONFIG4_JSON); + const StatementIndex stindex = MySqlSrvConfigMgr::GET_JSON_CONFIGURATION4; + SrvConfigInfoPtr result = getConfigCommon(stindex); + result->json_data_ = result->generic_data_; + result->generic_data_ = ""; + return result; +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getJsonConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_CONFIG6_JSON); + const StatementIndex stindex = MySqlSrvConfigMgr::GET_JSON_CONFIGURATION6; + SrvConfigInfoPtr result = getConfigCommon(stindex); + result->json_data_ = result->generic_data_; + result->generic_data_ = ""; + return result; +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getGenericConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_CONFIG4_GENERIC); + + const StatementIndex stindex = MySqlSrvConfigMgr::GET_GENERIC_CONFIGURATION4; + return getConfigCommon(stindex); +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getGenericConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SRV_CONFIG6_GENERIC); + + const StatementIndex stindex = MySqlSrvConfigMgr::GET_GENERIC_CONFIGURATION6; + return getConfigCommon(stindex); +} + +SrvConfigInfoPtr MySqlSrvConfigMgr::getConfigCommon(StatementIndex stindex) const { + char config_id[37]; // SQL type CHAR(36) + NULL character + MYSQL_TIME db_timestamp; + // The configuration data is too big to keep it on the thread's stack + std::vector config_data( + CONFIG_DATA_SIZE); // SQL type: LONGTEXT(4 GiB) + NULL character + // We limit this data to 60 MB + NULL character + + // Execute the statement + int status = mysql_stmt_execute(conn_.statements_[stindex]); + checkError(status, stindex, "unable to execute"); + + // Bind the output of the statement to the appropriate variables. + MYSQL_BIND bind[3]; + memset(bind, 0, sizeof(bind)); + + unsigned long config_id_length = sizeof(config_id); + bind[0].buffer_type = MYSQL_TYPE_STRING; + bind[0].buffer = &config_id[0]; + bind[0].buffer_length = config_id_length; + bind[0].length = &config_id_length; + + bind[1].buffer_type = MYSQL_TYPE_TIMESTAMP; + bind[1].buffer = reinterpret_cast(&db_timestamp); + bind[1].buffer_length = sizeof(db_timestamp); + + unsigned long config_data_length = config_data.size(); + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = &config_data[0]; + bind[2].buffer_length = config_data_length; + bind[2].length = &config_data_length; + + // overhead of going back and forth between client and server. + // status = mysql_stmt_store_result(conn_.statements_[stindex]); + status = mysql_stmt_bind_result(conn_.statements_[stindex], bind); + checkError(status, stindex, "unable to set up for storing all results"); + + // Fetch the data and set up the "release" object to release associated + // resources when this method exits then retrieve the data. + MySqlFreeResult fetch_release(conn_.statements_[stindex]); + status = mysql_stmt_fetch(conn_.statements_[stindex]); + if (status == MYSQL_NO_DATA) { + return SrvConfigInfoPtr(); + } + checkError(status, stindex, "unable to obtain result set"); + + time_t timestamp; + MySqlConnection::convertFromDatabaseTime(db_timestamp, 0, timestamp); + + SrvConfigInfoPtr srvConf(boost::make_shared()); + srvConf->config_id_ = std::string(&config_id[0], config_id_length); + srvConf->timestamp_ = timestamp; + srvConf->generic_data_ = std::string(&config_data[0], config_data_length); + + return srvConf; +} + +bool MySqlSrvConfigMgr::insertConfig4(const std::string& json_data, + const std::string& generic_data) const { + boost::uuids::random_generator gen; + boost::uuids::uuid unique_id = gen(); + std::string config_id = boost::lexical_cast(unique_id); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_INSERT_SRV_CONFIG4) + .arg(config_id); + + const StatementIndex stindex = MySqlSrvConfigMgr::INSERT_CONFIGURATION4; + return insertConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool MySqlSrvConfigMgr::insertConfig6(const std::string& json_data, + const std::string& generic_data) const { + boost::uuids::random_generator gen; + boost::uuids::uuid unique_id = gen(); + std::string config_id = boost::lexical_cast(unique_id); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_INSERT_SRV_CONFIG6) + .arg(config_id); + + const StatementIndex stindex = MySqlSrvConfigMgr::INSERT_CONFIGURATION6; + return insertConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool MySqlSrvConfigMgr::insertConfigCommon(StatementIndex stindex, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + try { + MYSQL_BIND bind[4]; // Bind array + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type = MYSQL_TYPE_STRING; + bind[0].buffer = const_cast(config_id.c_str()); + bind[0].buffer_length = config_id.length(); + bind[0].is_null = 0; + + MYSQL_TIME expire_time; + conn_.convertToDatabaseTime(time(NULL), expire_time); + bind[1].buffer_type = MYSQL_TYPE_TIMESTAMP; + bind[1].buffer = reinterpret_cast(&expire_time); + bind[1].buffer_length = sizeof(expire_time); + bind[1].is_null = 0; + + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = const_cast(json_data.c_str()); + bind[2].buffer_length = json_data.length(); + bind[2].is_null = 0; + + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = const_cast(generic_data.c_str()); + bind[3].buffer_length = generic_data.length(); + bind[3].is_null = 0; + + int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]); + checkError(status, stindex, "unable to bind parameters"); + + // Execute the statement + status = mysql_stmt_execute(conn_.statements_[stindex]); + if (status != 0) { + // Failure: check for the special case of duplicate entry. If this is + // the case, we return false to indicate that the row was not added. + // Otherwise we throw an exception. + if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) { + return (false); + } + checkError(status, stindex, "unable to execute"); + } + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not create bind array to insert configuration " + << ", reason: " << ex.what()); + } + + // Insert succeeded + return true; +} + +bool MySqlSrvConfigMgr::updateConfig4(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4) + .arg(config_id); + + const StatementIndex stindex = MySqlSrvConfigMgr::UPDATE_CONFIGURATION4; + return updateConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool MySqlSrvConfigMgr::updateConfig6(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6) + .arg(config_id); + + const StatementIndex stindex = MySqlSrvConfigMgr::UPDATE_CONFIGURATION6; + return updateConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool MySqlSrvConfigMgr::updateConfigCommon(StatementIndex stindex, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + try { + MYSQL_BIND bind[4]; // Bind array + memset(bind, 0, sizeof(bind)); + + MYSQL_TIME expire_time; + conn_.convertToDatabaseTime(time(NULL), expire_time); + bind[0].buffer_type = MYSQL_TYPE_TIMESTAMP; + bind[0].buffer = reinterpret_cast(&expire_time); + bind[0].buffer_length = sizeof(expire_time); + bind[0].is_null = 0; + + bind[1].buffer_type = MYSQL_TYPE_STRING; + bind[1].buffer = const_cast(json_data.c_str()); + bind[1].buffer_length = json_data.length(); + bind[1].is_null = 0; + + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = const_cast(generic_data.c_str()); + bind[2].buffer_length = generic_data.length(); + bind[2].is_null = 0; + + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = const_cast(config_id.c_str()); + bind[3].buffer_length = config_id.length(); + bind[3].is_null = 0; + + int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]); + checkError(status, stindex, "unable to bind parameters"); + + // Execute the statement + status = mysql_stmt_execute(conn_.statements_[stindex]); + if (status != 0) { + // Failure: check for the special case of duplicate entry. If this is + // the case, we return false to indicate that the row was not added. + // Otherwise we throw an exception. + if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) { + return (false); + } + checkError(status, stindex, "unable to execute"); + } + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not create bind to update configuration: " + << ", reason: " << ex.what()); + } + + // Update succeeded + return true; +} + +void MySqlSrvConfigMgr::checkError(int status, StatementIndex index, const char* what) const { + if (status != 0) { + switch (mysql_errno(conn_.mysql_)) { + // These are the ones we consider fatal. Remember this method is + // used to check errors of API calls made subsequent to successfully + // connecting. Errors occuring while attempting to connect are + // checked in the connection code. An alternative would be to call + // mysql_ping() - assuming autoreconnect is off. If that fails + // then we know connection is toast. + case CR_SERVER_GONE_ERROR: + case CR_SERVER_LOST: + case CR_OUT_OF_MEMORY: + case CR_CONNECTION_ERROR: + // We're exiting on fatal + LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_FATAL_ERROR) + .arg(what) + .arg(conn_.text_statements_[index]) + .arg(mysql_error(conn_.mysql_)) + .arg(mysql_errno(conn_.mysql_)); + exit(-1); + + default: + // Connection is ok, so it must be an SQL error + isc_throw(DbOperationError, what << " for <" << conn_.text_statements_[index] + << ">, reason: " << mysql_error(conn_.mysql_) + << " (error code " << mysql_errno(conn_.mysql_) + << ")"); + } + } +} + +bool MySqlSrvConfigMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_BEGIN_TRANSACTION); + if (mysql_query(conn_.mysql_, "START TRANSACTION")) { + isc_throw(DbOperationError, "start transaction failed: " << mysql_error(conn_.mysql_)); + } + + return true; +} + +void MySqlSrvConfigMgr::commit() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT); + if (mysql_commit(conn_.mysql_) != 0) { + isc_throw(DbOperationError, "commit failed: " << mysql_error(conn_.mysql_)); + } +} + +void MySqlSrvConfigMgr::rollback() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK); + if (mysql_rollback(conn_.mysql_) != 0) { + isc_throw(DbOperationError, "rollback failed: " << mysql_error(conn_.mysql_)); + } +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/mysql_srv_config_mgr.h b/src/lib/dhcpsrv/mysql_srv_config_mgr.h new file mode 100644 index 0000000000..3c2224c62f --- /dev/null +++ b/src/lib/dhcpsrv/mysql_srv_config_mgr.h @@ -0,0 +1,349 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MYSQL_CONFIGURATION_MGR_H +#define MYSQL_CONFIGURATION_MGR_H + +#include +#include + +#include + +namespace isc { +namespace dhcp { + +class MySqlSrvConfigMgr : public SrvConfigMgr { +public: + /// @brief Constructor + /// + /// Uses the following keywords in the parameters passed to it to + /// connect to the database: + /// - name - Name of the database to which to connect (mandatory) + /// - host - Host to which to connect (optional, defaults to "localhost") + /// - user - Username under which to connect (optional) + /// - password - Password for "user" on the database (optional) + /// + /// If the database is successfully opened, the version number in the + /// schema_version table will be checked against hard-coded value in + /// the implementation file. + /// + /// Finally, all the SQL commands are pre-compiled. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + /// + /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given + /// @throw isc::dhcp::DbOpenError Error opening the database + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + MySqlSrvConfigMgr(const DatabaseConnection::ParameterMap& parameters); + + /// @brief Destructor (closes database) + virtual ~MySqlSrvConfigMgr(); + + /// @brief Local version of getDBVersion() class method + static std::string getDBVersion(); + + /// @brief Adds if not exists or updates and existing DHCP V4 server + /// configuration + /// + /// One database contains server configuration data only for one kea server. + /// + /// @param old_config_timestamp If there is already a server configuration in + /// the database + /// then the configuration's timestamp from database is compared with + /// the provided + /// one. The update takes place only if the timestamps are the same. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @result true if the configuration was added/updated, false if not. + virtual bool updateConfig4(int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Adds if not exists or updates and existing DHCP V6 server + /// configuration + /// + /// One database contains server configuration data only for one kea server. + /// + /// @param old_config_timestamp If there is already a server configuration in + /// the database + /// then the configuration's timestamp from database is compared with + /// the provided + /// one. The update takes place only if the timestamps are the same. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added/updated, false if not. + virtual bool updateConfig6(int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Retrieves timestamp for the DHCPv4 shard server configuration. + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigInfoPtr getConfig4Timestamp() const; + + /// @brief Retrieves timestamp for the DHCPv6 shard server configuration. + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigInfoPtr getConfig6Timestamp() const; + + /// @brief Retrieves the configuration of the DHCPv4 server belonging to the + /// current shard in JSON format. + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getJsonConfig4() const; + + /// @brief Retrieves the configuration of the DHCPv6 server belonging to the + /// current shard in JSON format. + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getJsonConfig6() const; + + /// @brief Retrieves the configuration of the DHCPv4 server belonging to the + /// current shard in generic format (e.g. YAML). + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getGenericConfig4() const; + + /// @brief Retrieves the configuration of the DHCPv6 server belonging to the + /// current shard in generic format (e.g. YAML). + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getGenericConfig6() const; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction(); + + /// @brief Commit Transactions + /// + /// Commits all pending database operations. On databases that don't + /// support transactions, this is a no-op. + /// + /// @throw DbOperationError If the commit failed. + virtual void commit(); + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. On databases that don't + /// support transactions, this is a no-op. + /// + /// @throw DbOperationError If the rollback failed. + virtual void rollback(); + + /// @brief Returns backend version. + /// + /// @return Version number as a pair of unsigned integers. "first" is the + /// major version number, "second" the minor number. + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + virtual VersionPair getVersion() const; + + /// @brief Return backend type + /// + /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) + /// + /// @return Type of the backend. + virtual std::string getType() const { + return (std::string("mysql")); + } + + /// @brief Statement Tags + /// + /// The contents of the enum are indexes into the list of SQL statements + enum StatementIndex { + GET_VERSION, // Obtain version number + GET_CONFIGURATION4_TIMESTAMP, // Get the timestamp of the v4 stored + // configuration + GET_CONFIGURATION6_TIMESTAMP, // Get the timestamp of the v6 stored + // configuration + GET_JSON_CONFIGURATION4, // Get only JSON server configuration v4 + GET_JSON_CONFIGURATION6, // Get only JSON server configuration v6 + GET_GENERIC_CONFIGURATION4, // Get only GENERIC server configuration v4 + GET_GENERIC_CONFIGURATION6, // Get only GENERIC server configuration v6 + INSERT_CONFIGURATION4, // Add server configuration v4 + INSERT_CONFIGURATION6, // Add server configuration v6 + UPDATE_CONFIGURATION4, // Update server configuration v4 + UPDATE_CONFIGURATION6, // Update server configuration v6 + NUM_STATEMENTS // Number of statements + }; + +private: + /// @brief Inserts a new DHCPv4 server configuration. + /// + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added, false if not. + bool insertConfig4(const std::string& json_data, const std::string& generic_data) const; + + /// @brief Inserts a new DHCPv6 server configuration. + /// + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added, false if not. + bool insertConfig6(const std::string& json_data, const std::string& generic_data) const; + + /// @brief Updates an existing DHCPv4 server configuration. + /// + /// + /// @param version The new configuration's version. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added/updated, false if not. + bool updateConfig4(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Updates an existing DHCPv6 server configuration. + /// + /// + /// @param version The new configuration's version. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added/updated, false if not. + bool updateConfig6(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Check Error and Throw Exception + /// + /// Virtually all MySQL functions return a status which, if non-zero, + /// indicates an error. This function centralizes the error checking + /// code. + /// + /// It is used to determine whether or not the function succeeded, and + /// in the event of failures, decide whether or not those failures are + /// recoverable. + /// + /// If the error is recoverable, the method will throw a DbOperationError. + /// In the error is deemed unrecoverable, such as a loss of connectivity + /// with the server, this method will log the error and call exit(-1); + /// + /// @todo Calling exit() is viewed as a short term solution for Kea 1.0. + /// Two tickets are likely to alter this behavior, first is #3639, which + /// calls for the ability to attempt to reconnect to the database. The + /// second ticket, #4087 which calls for the implementation of a generic, + /// FatalException class which will propagate outward. + /// + /// @param status Status code: non-zero implies an error + /// @param index Index of statement that caused the error + /// @param what High-level description of the error + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + void checkError(int status, StatementIndex index, const char* what) const; + + /// @brief Retrieves synchronization timestamps from the shard database. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// + /// @return a configuration containing the timestamp in question + SrvConfigInfoPtr getConfigTimestampCommon(StatementIndex stindex) const; + + /// @brief Retrieves a single server configuration either for DHCPv4 or for + /// DHCPv6 from the current shard. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// + /// @return the single retrieved configuration + SrvConfigInfoPtr getConfigCommon(StatementIndex stindex) const; + + /// @brief Adds server configuration either for DHCPv4 or for DHCPv6. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// @param config_id unique ID for configuration + /// @param json_data JSON configuration being inserted + /// @param generic_data generic configuration (e.g. YAML) being inserted + /// + /// @return true, if configuration has been successfully added, false + /// otherwise + bool insertConfigCommon(StatementIndex stindex, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Updates server configuration either for DHCPv4 or for DHCPv6. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// @param config_id unique ID for configuration + /// @param json_data JSON configuration to update the current configuration + /// @param generic_data generic configuration (e.g. YAML) to update the + /// current configuration + /// + /// @return true, if configuration has been successfully updated, false + /// otherwise + bool updateConfigCommon(StatementIndex stindex, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + // Members + /// @brief MySQL connection + mutable MySqlConnection conn_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // MYSQL_CONFIGURATION_MGR_H diff --git a/src/lib/dhcpsrv/network.cc b/src/lib/dhcpsrv/network.cc index fbd7d35539..4a1136bb0a 100644 --- a/src/lib/dhcpsrv/network.cc +++ b/src/lib/dhcpsrv/network.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcpsrv/network_state.cc b/src/lib/dhcpsrv/network_state.cc index fdf5d17f6c..a13cc609f3 100644 --- a/src/lib/dhcpsrv/network_state.cc +++ b/src/lib/dhcpsrv/network_state.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc index 1665b74f7c..5e90cc7e8d 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc @@ -65,8 +65,9 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, // 2. Update the copy with the passed keywords. BOOST_FOREACH(ConfigPair param, database_config->mapValue()) { try { - if ((param.first == "persist") || (param.first == "readonly") || - (param.first == "tcp-nodelay")) { + if ((param.first == "persist") || + (param.first == "tcp-nodelay") || + (param.first == "readonly")) { values_copy[param.first] = (param.second->boolValue() ? "true" : "false"); @@ -106,6 +107,15 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, boost::lexical_cast(port); } else { + // all remaining string parameters + // type + // user + // password + // host + // name + // contact-points + // keyspace + // protocol values_copy[param.first] = param.second->stringValue(); } } catch (const isc::data::TypeError& ex) { @@ -172,14 +182,14 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, << " (" << value->getPosition() << ")"); } - // Check that the max-reconnect-retries reasonable. + // Check that the max-reconnect-tries is reasonable. if (max_reconnect_tries < 0) { ConstElementPtr value = database_config->get("max-reconnect-tries"); isc_throw(DhcpConfigError, "max-reconnect-tries cannot be less than zero: " << " (" << value->getPosition() << ")"); } - // Check that the reconnect-wait-time reasonable. + // Check that the reconnect-wait-time is reasonable. if ((reconnect_wait_time < 0) || (reconnect_wait_time > std::numeric_limits::max())) { ConstElementPtr value = database_config->get("reconnect-wait-time"); @@ -189,17 +199,17 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, } // Check that request_timeout value makes sense. - if ((reconnect_wait_time < 0) || - (reconnect_wait_time > std::numeric_limits::max())) { - ConstElementPtr value = database_config->get("reconnect-wait-time"); - isc_throw(DhcpConfigError, "reconnect-wait-time " << reconnect_wait_time + if ((request_timeout < 0) || + (request_timeout > std::numeric_limits::max())) { + ConstElementPtr value = database_config->get("request-timeout"); + isc_throw(DhcpConfigError, "request-timeout " << request_timeout << " must be in range 0...MAX_UINT32 (4294967295) " << " (" << value->getPosition() << ")"); } // Check that tcp_keepalive value makes sense. if ((tcp_keepalive < 0) || (tcp_keepalive > std::numeric_limits::max())) { - ConstElementPtr value = database_config->get("reconnect-wait-time"); + ConstElementPtr value = database_config->get("tcp-keepalive"); isc_throw(DhcpConfigError, "tcp-keepalive " << tcp_keepalive << " must be in range 0...MAX_UINT32 (4294967295) " << " (" << value->getPosition() << ")"); @@ -216,6 +226,10 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, cfg_db->setLeaseDbAccessString(getDbAccessString()); } else if (type_ == DBType::HOSTS_DB) { cfg_db->setHostDbAccessString(getDbAccessString(), false); + } else if (type_ == DBType::CONFIG_DB) { + cfg_db->setSrvCfgDbAccessString(getDbAccessString()); + } else if (type_ == DBType::MASTER_DB) { + cfg_db->setSrvCfgMasterDbAccessString(getDbAccessString()); } } diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.h b/src/lib/dhcpsrv/parsers/dbaccess_parser.h index e2013cc17e..d800b5fced 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.h +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,7 @@ namespace dhcp { /// map under the top-level "lease-database" element, and comprises a map of /// strings. /// -/// Only the "type" sub-element is mandatory: the remaining sub-elements +/// Only the "type" sub-element is mandatory: the remaining sub-elements /// depend on the database chosen. class DbAccessParser: public isc::data::SimpleParser { public: @@ -95,8 +96,8 @@ class DbAccessParser: public isc::data::SimpleParser { DBType type_; ///< Database type (leases or hosts) }; -}; // namespace dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc #endif // DBACCESS_PARSER_H diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index aec0ed2645..b57c180fe7 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -230,7 +230,7 @@ OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_l //****************************** RelayInfoParser ******************************** RelayInfoParser::RelayInfoParser(const Option::Universe& family) : family_(family) { -}; +} void RelayInfoParser::parse(const isc::dhcp::Network::RelayInfoPtr& relay_info, @@ -739,7 +739,7 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params, // directly over specified network interface. std::string iface = getString(params, "interface"); if (!iface.empty()) { - if (!IfaceMgr::instance().getIface(iface)) { + if (IfaceMgr::instance().isServerMode() && !IfaceMgr::instance().getIface(iface)) { ConstElementPtr error = params->get("interface"); isc_throw(DhcpConfigError, "Specified network interface name " << iface << " for subnet " << subnet4->toText() @@ -1137,7 +1137,7 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params, // Get interface name. If it is defined, then the subnet is available // directly over specified network interface. if (!iface.empty()) { - if (!IfaceMgr::instance().getIface(iface)) { + if (IfaceMgr::instance().isServerMode() && !IfaceMgr::instance().getIface(iface)) { ConstElementPtr error = params->get("interface"); isc_throw(DhcpConfigError, "Specified network interface name " << iface << " for subnet " << subnet6->toText() @@ -1418,5 +1418,5 @@ D2ClientConfigParser::setAllDefaults(isc::data::ConstElementPtr d2_config) { return (SimpleParser::setDefaults(mutable_d2, D2_CLIENT_CONFIG_DEFAULTS)); } -}; // namespace dhcp -}; // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index b323eb4b87..4a218ac8d7 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h @@ -853,7 +853,7 @@ class D2ClientConfigParser : public isc::data::SimpleParser { getMode(isc::data::ConstElementPtr scope, const std::string& name); }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc #endif // DHCP_PARSERS_H diff --git a/src/lib/dhcpsrv/parsers/option_data_parser.cc b/src/lib/dhcpsrv/parsers/option_data_parser.cc index 27fd9ab41f..9f4e138560 100644 --- a/src/lib/dhcpsrv/parsers/option_data_parser.cc +++ b/src/lib/dhcpsrv/parsers/option_data_parser.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcpsrv/parsers/shared_network_parser.cc b/src/lib/dhcpsrv/parsers/shared_network_parser.cc index 693b0f271a..2e77490f9a 100644 --- a/src/lib/dhcpsrv/parsers/shared_network_parser.cc +++ b/src/lib/dhcpsrv/parsers/shared_network_parser.cc @@ -7,6 +7,8 @@ #ifndef SHARED_NETWORK_PARSER_H #define SHARED_NETWORK_PARSER_H +#include + #include #include #include diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.cc b/src/lib/dhcpsrv/parsers/simple_parser4.cc index e766e96323..8a4d08b43e 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser4.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser4.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include @@ -228,5 +230,5 @@ size_t SimpleParser4::deriveParameters(isc::data::ElementPtr global) { return (cnt); } -}; -}; +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.cc b/src/lib/dhcpsrv/parsers/simple_parser6.cc index f7ca2dba16..cc7ae8d0d7 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser6.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser6.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include @@ -209,5 +211,5 @@ size_t SimpleParser6::deriveParameters(isc::data::ElementPtr global) { return (cnt); } -}; -}; +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/pgsql_connection.cc b/src/lib/dhcpsrv/pgsql_connection.cc index a64ce7f779..ff6b83db84 100644 --- a/src/lib/dhcpsrv/pgsql_connection.cc +++ b/src/lib/dhcpsrv/pgsql_connection.cc @@ -103,7 +103,10 @@ PgSqlTransaction::PgSqlTransaction(PgSqlConnection& conn) PgSqlTransaction::~PgSqlTransaction() { // If commit() wasn't explicitly called, rollback. if (!committed_) { - conn_.rollback(); + try { + conn_.rollback(); + } catch (...) { + } } } diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc index ae632ef22b..b5b0950ddf 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.cc +++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -24,8 +25,9 @@ #include #include -#include #include +#include +#include using namespace isc; using namespace isc::asiolink; @@ -1236,7 +1238,7 @@ class PgSqlOptionExchange : public PgSqlExchange { OptionPtr option_; }; -} // end of anonymous namespace +} // namespace namespace isc { namespace dhcp { @@ -1405,7 +1407,7 @@ class PgSqlHostDataSourceImpl { /// /// @throw isc::dhcp::DbOperationError An operation on the open database /// has failed. - std::pair getVersion() const; + VersionPair getVersion() const; /// @brief Pointer to the object representing an exchange which /// can be used to retrieve hosts and DHCPv4 options. @@ -1693,7 +1695,7 @@ TaggedStatementArray tagged_statements = { { } }; -}; // end anonymous namespace +} // namespace PgSqlHostDataSourceImpl:: PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters) @@ -1893,20 +1895,21 @@ getHost(const SubnetID& subnet_id, return (result); } -std::pair PgSqlHostDataSourceImpl::getVersion() const { +VersionPair +PgSqlHostDataSourceImpl::getVersion() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_HOST_DB_GET_VERSION); PgSqlResult r(PQexecPrepared(conn_, "get_version", 0, NULL, NULL, NULL, 0)); conn_.checkStatementError(r, tagged_statements[GET_VERSION]); - uint32_t version; - PgSqlExchange::getColumnValue(r, 0, 0, version); + uint32_t major; + PgSqlExchange::getColumnValue(r, 0, 0, major); uint32_t minor; PgSqlExchange::getColumnValue(r, 0, 0, minor); - return (std::make_pair(version, minor)); + return (std::make_pair(major, minor)); } void @@ -2253,7 +2256,8 @@ PgSqlHostDataSource::get6(const SubnetID& subnet_id, // Miscellaneous database methods. -std::string PgSqlHostDataSource::getName() const { +std::string +PgSqlHostDataSource::getName() const { std::string name = ""; try { name = impl_->conn_.getParameter("name"); @@ -2263,15 +2267,31 @@ std::string PgSqlHostDataSource::getName() const { return (name); } -std::string PgSqlHostDataSource::getDescription() const { +std::string +PgSqlHostDataSource::getDescription() const { return (std::string("Host data source that stores host information" "in PostgreSQL database")); } -std::pair PgSqlHostDataSource::getVersion() const { +VersionPair +PgSqlHostDataSource::getVersion() const { return(impl_->getVersion()); } +void +PgSqlHostDataSource::syncReservations() { + HostCollection hosts = + CfgMgr::instance().getStagingCfg()->getCfgHosts()->getAll(); + for (HostCollection::const_iterator it = hosts.begin(); + it != hosts.end(); ++it) { + try { + add(*it); + } catch(const DuplicateEntry& exception) { + // Don't insert duplicates. + } + } +} + void PgSqlHostDataSource::commit() { // If operating in read-only mode, throw exception. @@ -2286,5 +2306,5 @@ PgSqlHostDataSource::rollback() { impl_->conn_.rollback(); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.h b/src/lib/dhcpsrv/pgsql_host_data_source.h index 5a270b449c..bb784b0095 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.h +++ b/src/lib/dhcpsrv/pgsql_host_data_source.h @@ -323,7 +323,7 @@ class PgSqlHostDataSource: public BaseHostDataSource { /// /// @throw isc::dhcp::DbOperationError An operation on the open database /// has failed. - virtual std::pair getVersion() const; + virtual VersionPair getVersion() const; /// @brief Commit Transactions /// @@ -335,6 +335,12 @@ class PgSqlHostDataSource: public BaseHostDataSource { /// Rolls back all pending database operations. virtual void rollback(); + /// @brief Sync database tables with kea.conf + /// + /// Truncates database tables, retrieves reservations read from kea.conf and + /// inserts coresponding hosts, reservations and options in the database. + virtual void syncReservations(); + private: /// @brief Pointer to the implementation of the @ref PgSqlHostDataSource. PgSqlHostDataSourceImpl* impl_; diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index 1929bae8ed..31a90da066 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -255,7 +255,7 @@ PgSqlTaggedStatement tagged_statements[] = { { 0, { OID_NONE }, "all_lease4_stats", "SELECT subnet_id, state, leases as state_count" - " FROM lease4_stat ORDER BY subnet_id, state"}, + " FROM lease4_stat ORDER BY subnet_id, state" }, // SUBNET_LEASE4_STATS { 1, { OID_INT8 }, @@ -263,7 +263,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT subnet_id, state, leases as state_count" " FROM lease4_stat " " WHERE subnet_id = $1 " - " ORDER BY state"}, + " ORDER BY state" }, // SUBNET_RANGE_LEASE4_STATS { 2, { OID_INT8, OID_INT8 }, @@ -271,7 +271,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT subnet_id, state, leases as state_count" " FROM lease4_stat " " WHERE subnet_id >= $1 and subnet_id <= $2 " - " ORDER BY subnet_id, state"}, + " ORDER BY subnet_id, state" }, // ALL_LEASE6_STATS, { 0, { OID_NONE }, @@ -1011,8 +1011,8 @@ PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) << " does not match expected count:" << NUM_STATEMENTS); } - pair code_version(PG_SCHEMA_VERSION_MAJOR, PG_SCHEMA_VERSION_MINOR); - pair db_version = getVersion(); + VersionPair code_version(PG_SCHEMA_VERSION_MAJOR, PG_SCHEMA_VERSION_MINOR); + VersionPair db_version = getVersion(); if (code_version != db_version) { isc_throw(DbOpenError, "PostgreSQL schema version mismatch: need version: " @@ -1676,7 +1676,7 @@ PgSqlLeaseMgr::getDescription() const { return (string("PostgreSQL Database")); } -pair +VersionPair PgSqlLeaseMgr::getVersion() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_VERSION); @@ -1685,9 +1685,9 @@ PgSqlLeaseMgr::getVersion() const { conn_.checkStatementError(r, tagged_statements[GET_VERSION]); istringstream tmp; - uint32_t version; + uint32_t major; tmp.str(PQgetvalue(r, 0, 0)); - tmp >> version; + tmp >> major; tmp.str(""); tmp.clear(); @@ -1695,7 +1695,22 @@ PgSqlLeaseMgr::getVersion() const { tmp.str(PQgetvalue(r, 0, 1)); tmp >> minor; - return (make_pair(version, minor)); + return (make_pair(major, minor)); +} + +bool +PgSqlLeaseMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_BEGIN_TRANSACTION); + PGresult* r = PQexec(conn_, "START TRANSACTION"); + if (PQresultStatus(r) != PGRES_COMMAND_OK) { + const char* error_message = PQerrorMessage(conn_); + PQclear(r); + isc_throw(DbOperationError, "start transaction failed: " << error_message); + } + + PQclear(r); + + return true; } void @@ -1708,5 +1723,5 @@ PgSqlLeaseMgr::rollback() { conn_.rollback(); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index e36abf02c3..1fa653ef20 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -447,7 +447,13 @@ class PgSqlLeaseMgr : public LeaseMgr { /// /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - virtual std::pair getVersion() const; + virtual VersionPair getVersion() const; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction(); /// @brief Commit Transactions /// diff --git a/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc new file mode 100644 index 0000000000..57702b8b38 --- /dev/null +++ b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc @@ -0,0 +1,648 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace { + +using namespace isc::dhcp; + +/// @brief Prepared statements +std::array tagged_statements = {{ + // INSERT_SERVER_CONFIG4 + {5, + {OID_VARCHAR, OID_TIMESTAMP, OID_BYTEA, OID_BYTEA, OID_VARCHAR}, + "INSERT_SERVER_CONFIG4", + "INSERT INTO server_configuration4" + " (instance_id, timestamp, server_config, config_database, config_database_name)" + " VALUES ($1, $2, $3, $4, $5)"}, + + // INSERT_SERVER_CONFIG6 + {5, + {OID_VARCHAR, OID_TIMESTAMP, OID_BYTEA, OID_BYTEA, OID_VARCHAR}, + "INSERT_SERVER_CONFIG6", + "INSERT INTO server_configuration6" + " (instance_id, timestamp, server_config, config_database, config_database_name)" + " VALUES ($1, $2, $3, $4, $5)"}, + + // GET_CONFIGURATION4_BY_SRV_ID + {1, + {OID_VARCHAR}, + "GET_CONFIGURATION4_BY_SRV_ID", + "SELECT instance_id, extract(epoch from timestamp)::bigint, server_config, config_database, " + "config_database_name" + " FROM server_configuration4" + " WHERE instance_id = $1"}, + + // GET_CONFIGURATION6_BY_SRV_ID + {1, + {OID_VARCHAR}, + "GET_CONFIGURATION6_BY_SRV_ID", + "SELECT instance_id, extract(epoch from timestamp)::bigint, server_config, config_database, " + "config_database_name" + " FROM server_configuration6" + " WHERE instance_id = $1"}, + + // GET_CONFIGURATION4_BY_SHARD_DB + {1, + {OID_VARCHAR}, + "GET_CONFIGURATION4_BY_SHARD_DB", + "SELECT instance_id, extract(epoch from timestamp)::bigint, server_config, config_database, " + "config_database_name" + " FROM server_configuration4" + " WHERE config_database_name = $1"}, + + // GET_CONFIGURATION6_BY_SHARD_DB + {1, + {OID_VARCHAR}, + "GET_CONFIGURATION6_BY_SHARD_DB", + "SELECT instance_id, extract(epoch from timestamp)::bigint, server_config, config_database, " + "config_database_name" + " FROM server_configuration6" + " WHERE config_database_name = $1"}, + + // GET_CONFIGURATION4_TIMESTAMP + {1, + {OID_VARCHAR}, + "GET_CONFIGURATION4_TIMESTAMP", + "SELECT extract(epoch from timestamp)::bigint" + " FROM server_configuration4" + " WHERE instance_id = $1"}, + + // GET_CONFIGURATION6_TIMESTAMP + {1, + {OID_VARCHAR}, + "GET_CONFIGURATION6_TIMESTAMP", + "SELECT extract(epoch from timestamp)::bigint" + " FROM server_configuration6" + " WHERE instance_id = $1"}, + + // DELETE_SERVER_CONFIG4 + {0, + {OID_NONE}, + "DELETE_SERVER_CONFIG4", + "TRUNCATE server_configuration4"}, + + // DELETE_SERVER_CONFIG6, + {0, + {OID_NONE}, + "DELETE_SERVER_CONFIG6", + "TRUNCATE server_configuration6"}, + + // GET_SERVERS_CONFIG4_SHARDS_NAME + {0, + {OID_NONE}, + "GET_SERVERS_CONFIG4_SHARDS_NAME", + "SELECT config_database_name" + " FROM server_configuration4"}, + + // GET_SERVERS_CONFIG6_SHARDS_NAME + {0, + {OID_NONE}, + "GET_SERVERS_CONFIG6_SHARDS_NAME", + "SELECT config_database_name" + " FROM server_configuration6"}, + + // GET_VERSION + {0, + {OID_NONE}, + "GET_VERSION", + "SELECT version, minor" + " FROM master_schema_version"}, +}}; + +} // namespace + +namespace isc { +namespace dhcp { + +/// @brief Manages exchanging of master configurations with PostgreSQL. +class PgSqlMasterConfigExchange : public PgSqlExchange { +public: + /// @brief Default constructor + PgSqlMasterConfigExchange() : config_(boost::make_shared()) { + BOOST_STATIC_ASSERT(COLUMN_COUNT == 5); + + // Set column names (for error reporting). + columns_.push_back("instance_id"); + columns_.push_back("timestamp"); + columns_.push_back("server_config"); + columns_.push_back("config_database"); + columns_.push_back("config_database_name"); + } + + virtual ~PgSqlMasterConfigExchange() { + } + + /// @brief Creates the bind array for sending @ref SrvConfigMasterInfo data + /// to the database. + /// + /// Converts each @ref SrvConfigMasterInfo member into a + /// PostgreSQL-compatible type and adds it to the bind array. Note that the + /// array additions must occur in the same order that the SQL statement + /// specifies. By convention all columns in the table are explicitly listed + /// in the SQL statements in the same order as they occur in the table. + /// + /// @param instance_id ID of the server that has it's configuration written to the database + /// @param server_config actual configuration of the server that is written to the database + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// @param[out] bind_array array to populate with the configuration data + /// + /// @throw DbOperationError if bind_array cannot be populated + void createBindForSend(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name, + PsqlBindArray& bind_array) { + // Convert to PostgreSQL-compatible types. + config_->instance_id_ = instance_id; + timestamp_ = PgSqlExchange::convertToDatabaseTime(time(NULL)); + server_config_.assign(server_config.c_str(), server_config.c_str() + server_config.size()); + config_database_.assign(config_database.c_str(), + config_database.c_str() + config_database.size()); + config_->config_database_name_ = config_database_name; + + try { + // Bind to array. + bind_array.add(config_->instance_id_); + bind_array.add(timestamp_); + bind_array.add(server_config_); + bind_array.add(config_database_); + bind_array.add(config_->config_database_name_); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not create bind array for " + "server_configuration[4|6] with server ID: " + << instance_id << ", shard name: " << config_database_name + << ", server configuration: " << server_config + << ", reason: " << ex.what()); + } + } + + /// @brief Creates a @ref SrvConfigMasterInfo object from a given row in a + /// result set. + /// + /// @param r result set containing one or rows from the server_configuration[4|6] table + /// @param row row number within the result set from to create the @ref SrvConfigMasterInfo + /// object + /// + /// @return SrvConfigMasterInfoPtr to the newly created @ref SrvConfigMasterInfo object + /// + /// @throw DbOperationError if the server configuration object cannot be created + SrvConfigMasterInfoPtr convertFromDatabase(const PgSqlResult& r, const int row) { + SrvConfigMasterInfoPtr config = boost::make_shared(); + try { + // Retrieve column values. + size_t converted_bytes; + unsigned char* bytes; + + // instance_id + getColumnValue(r, row, INSTANCE_ID, config->instance_id_); + + // timestamp + std::string timestamp_s = getRawColumnValue(r, row, TIME_STAMP); + config->timestamp_ = convertFromDatabaseTime(timestamp_s); + + // server_config + bytes = PQunescapeBytea( + reinterpret_cast(PQgetvalue(r, row, SERVER_CONFIG)), + &converted_bytes); + + if (bytes) { + config->server_config_.assign(bytes, bytes + converted_bytes); + } + PQfreemem(bytes); + + // config_database + bytes = PQunescapeBytea( + reinterpret_cast(PQgetvalue(r, row, CONFIG_DATABASE)), + &converted_bytes); + + if (bytes) { + config->config_database_.assign(bytes, bytes + converted_bytes); + } + PQfreemem(bytes); + + // config_database_name + getColumnValue(r, row, CONFIG_DATABASE_NAME, config->config_database_name_); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not convert data to server " + "configuration object, reason: " + << ex.what()); + } + return config; + } + + // C++98 standard ensures enum values are consecutive starting with 0. + // Don't add ordinal numbers in order to facilitate further addition of enum + // values. N_OF_COLUMNS is always last. + enum Columns { + INSTANCE_ID, + TIME_STAMP, + SERVER_CONFIG, + CONFIG_DATABASE, + CONFIG_DATABASE_NAME, + COLUMN_COUNT + }; + + /// @brief Configuration object used when sending to the database. + /// Being a class member ensures that it remains in scope while any bindings + /// that refer to its contents are in use. + SrvConfigMasterInfoPtr config_; + + /// @brief Configuration specific members used for binding and conversion. + /// @{ + std::vector config_database_; + std::vector server_config_; + std::string timestamp_; + /// @} +}; + +PgSqlSrvConfigMasterMgr::PgSqlSrvConfigMasterMgr(const DatabaseConnection::ParameterMap& parameters) + : conn_(parameters), exchange_(new PgSqlMasterConfigExchange()) { + + // Open the database. + conn_.openDatabase(); + int i = 0; + for (auto const& it : tagged_statements) { + conn_.prepareStatement(it); + ++i; + } + + // Check that all statements have been prepared. + BOOST_ASSERT(i == NUM_STATEMENTS); +} + +PgSqlSrvConfigMasterMgr::~PgSqlSrvConfigMasterMgr() { +} + +std::string PgSqlSrvConfigMasterMgr::getDBVersion() { + std::stringstream tmp; + tmp << "PostgreSQL backend " << PG_SCHEMA_VERSION_MAJOR << "." << PG_SCHEMA_VERSION_MINOR; + return tmp.str(); +} + +VersionPair PgSqlSrvConfigMasterMgr::getVersion() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_VERSION); + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_VERSION; + + PgSqlResult r( + PQexecPrepared(conn_, tagged_statements[statement_index].name, 0, NULL, NULL, NULL, 0)); + conn_.checkStatementError(r, tagged_statements[statement_index]); + + std::istringstream tmp; + uint32_t major; + tmp.str(PQgetvalue(r, 0, 0)); + tmp >> major; + tmp.str(""); + tmp.clear(); + + uint32_t minor; + tmp.str(PQgetvalue(r, 0, 1)); + tmp >> minor; + + return (std::make_pair(major, minor)); +} + +bool PgSqlSrvConfigMasterMgr::addServerConfig4(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG4) + .arg(instance_id) + .arg(config_database_name); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG4; + return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, + config_database_name); +} + +bool PgSqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG6) + .arg(instance_id) + .arg(config_database_name); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG6; + return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, + config_database_name); +} + +bool PgSqlSrvConfigMasterMgr::clearServersConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG4); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG4; + PgSqlResult r( + PQexecPrepared(conn_, tagged_statements[statement_index].name, 0, NULL, NULL, NULL, 0)); + conn_.checkStatementError(r, tagged_statements[statement_index]); + + return true; +} + +bool PgSqlSrvConfigMasterMgr::clearServersConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG6); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG6; + PgSqlResult r( + PQexecPrepared(conn_, tagged_statements[statement_index].name, 0, NULL, NULL, NULL, 0)); + conn_.checkStatementError(r, tagged_statements[statement_index]); + + return true; +} + +SrvConfigMasterInfoPtr PgSqlSrvConfigMasterMgr::getConfig4(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4) + .arg(instance_id); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_CONFIGURATION4_BY_SRV_ID; + return getCommonServerConfigurationByInstanceId(statement_index, instance_id); +} + +SrvConfigMasterInfoPtr PgSqlSrvConfigMasterMgr::getConfig6(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6) + .arg(instance_id); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_CONFIGURATION6_BY_SRV_ID; + return getCommonServerConfigurationByInstanceId(statement_index, instance_id); +} + +bool PgSqlSrvConfigMasterMgr::getConfig4( + const std::string& config_database_name, + std::vector& server_configurations) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB) + .arg(config_database_name); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_CONFIGURATION4_BY_SHARD_DB; + return getCommonServerConfigurationByShardName(statement_index, config_database_name, + server_configurations); +} + +bool PgSqlSrvConfigMasterMgr::getConfig6( + const std::string& config_database_name, + std::vector& server_configurations) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB) + .arg(config_database_name); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_CONFIGURATION6_BY_SHARD_DB; + return getCommonServerConfigurationByShardName(statement_index, config_database_name, + server_configurations); +} + +SrvConfigMasterInfoPtr +PgSqlSrvConfigMasterMgr::getMasterConfig4Timestamp(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP) + .arg(instance_id); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_CONFIGURATION4_TIMESTAMP; + return getCommonTimestampFromMasterServerConfiguration(statement_index, instance_id); +} + +SrvConfigMasterInfoPtr +PgSqlSrvConfigMasterMgr::getMasterConfig6Timestamp(const std::string& instance_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP) + .arg(instance_id); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_CONFIGURATION6_TIMESTAMP; + return getCommonTimestampFromMasterServerConfiguration(statement_index, instance_id); +} + +bool PgSqlSrvConfigMasterMgr::getServersConfig4ShardsName( + std::set& shards_list) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_SERVERS_CONFIG4_SHARDS_NAME; + return getCommonShardNamesFromMasterServerConfiguration(statement_index, shards_list); +} + +bool PgSqlSrvConfigMasterMgr::getServersConfig6ShardsName( + std::set& shards_list) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME); + + const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::GET_SERVERS_CONFIG6_SHARDS_NAME; + return getCommonShardNamesFromMasterServerConfiguration(statement_index, shards_list); +} + +bool PgSqlSrvConfigMasterMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_BEGIN_TRANSACTION); + PGresult* r = PQexec(conn_, "START TRANSACTION"); + if (PQresultStatus(r) != PGRES_COMMAND_OK) { + const char* error_message = PQerrorMessage(conn_); + PQclear(r); + isc_throw(DbOperationError, "start transaction failed: " << error_message); + } + + PQclear(r); + + return true; +} + +void PgSqlSrvConfigMasterMgr::commit() { + conn_.commit(); +} + +void PgSqlSrvConfigMasterMgr::rollback() { + conn_.rollback(); +} + +bool PgSqlSrvConfigMasterMgr::addCommonServerConfiguration( + const StatementIndex statement_index, + const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const { + try { + // Bind to array. + PsqlBindArray bind_array; + exchange_->createBindForSend(instance_id, server_config, config_database, + config_database_name, bind_array); + + // Execute query. + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[statement_index].name, + tagged_statements[statement_index].nbparams, + &bind_array.values_[0], &bind_array.lengths_[0], + &bind_array.formats_[0], 0)); + + // Check for errors. + conn_.checkStatementError(r, tagged_statements[statement_index]); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Cannot add server configuration into the " + "master database, reason: " + << ex.what()); + } + + return true; +} + +SrvConfigMasterInfoPtr PgSqlSrvConfigMasterMgr::getCommonServerConfigurationByInstanceId( + const StatementIndex statement_index, const std::string& instance_id) const { + SrvConfigMasterInfoPtr config; + try { + // Bind to array. + PsqlBindArray bind_array; + bind_array.add(instance_id); + + // Execute query. + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[statement_index].name, + tagged_statements[statement_index].nbparams, + &bind_array.values_[0], &bind_array.lengths_[0], + &bind_array.formats_[0], 0)); + + // Check for errors. + conn_.checkStatementError(r, tagged_statements[statement_index]); + + // Populate shard configurations. + int rows = PQntuples(r); + for (int i = 0; i < rows; ++i) { + config = exchange_->convertFromDatabase(r, i); + } + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Cannot retrieve server configuration from " + "the master database, reason: " + << ex.what()); + } + + return config; +} + +bool PgSqlSrvConfigMasterMgr::getCommonServerConfigurationByShardName( + const StatementIndex statement_index, + const std::string& config_database_name, + std::vector& server_configurations) const { + + server_configurations.clear(); + + try { + // Bind to array. + PsqlBindArray bind_array; + bind_array.add(config_database_name); + + // Execute query. + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[statement_index].name, + tagged_statements[statement_index].nbparams, + &bind_array.values_[0], &bind_array.lengths_[0], + &bind_array.formats_[0], 0)); + + // Check for errors. + conn_.checkStatementError(r, tagged_statements[statement_index]); + + // Populate configuration object. + int rows = PQntuples(r); + for (int i = 0; i < rows; ++i) { + server_configurations.push_back(exchange_->convertFromDatabase(r, i)); + } + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Cannot retrieve server configuration from " + "the master database, reason: " + << ex.what()); + } + + return true; +} + +SrvConfigMasterInfoPtr PgSqlSrvConfigMasterMgr::getCommonTimestampFromMasterServerConfiguration( + const StatementIndex statement_index, const std::string& instance_id) const { + SrvConfigMasterInfoPtr config = boost::make_shared(); + try { + // Bind to array. + PsqlBindArray bind_array; + bind_array.add(instance_id); + + // Execute query. + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[statement_index].name, + tagged_statements[statement_index].nbparams, + &bind_array.values_[0], &bind_array.lengths_[0], + &bind_array.formats_[0], 0)); + + // Check for errors. + conn_.checkStatementError(r, tagged_statements[statement_index]); + int affected_rows = boost::lexical_cast(PQcmdTuples(r)); + if (affected_rows < 1) { + return config; + } + + // Populate timestamp. + std::istringstream tmp; + std::string timestamp_s; + + tmp.str(PQgetvalue(r, 0, 0)); + tmp >> timestamp_s; + + time_t timestamp = PgSqlExchange::convertFromDatabaseTime(timestamp_s); + + config->timestamp_ = timestamp; + } catch (const std::exception& ex) { + isc_throw(DbOperationError, + "Cannot retrieve timestamp from the master database, reason: " << ex.what()); + } + + return config; +} + +bool PgSqlSrvConfigMasterMgr::getCommonShardNamesFromMasterServerConfiguration( + const StatementIndex statement_index, std::set& shards) const { + shards.clear(); + + try { + // Execute query. + PgSqlResult r( + PQexecPrepared(conn_, tagged_statements[statement_index].name, 0, NULL, NULL, NULL, 0)); + + // Check for errors. + conn_.checkStatementError(r, tagged_statements[statement_index]); + int affected_rows = boost::lexical_cast(PQcmdTuples(r)); + if (affected_rows < 1) { + return false; + } + + // Populate shard names. + int rows = PQntuples(r); + for (int i = 0; i < rows; ++i) { + std::string config_database_name; + exchange_->getColumnValue(r, i, 0, config_database_name); + shards.insert(config_database_name); + } + } catch (const std::exception& ex) { + isc_throw(DbOperationError, + "Cannot retrieve shard names from the master database, reason: " << ex.what()); + } + + return true; +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h new file mode 100644 index 0000000000..9f44c2d640 --- /dev/null +++ b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h @@ -0,0 +1,292 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PGSQL_SRV_CONFIG_MASTER_MGR_H +#define PGSQL_SRV_CONFIG_MASTER_MGR_H + +#include +#include + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Forward declaration to the exchange. +class PgSqlMasterConfigExchange; + +/// @brief Exchange used to interact with the master server configuration table +/// from the database +class PgSqlSrvConfigMasterMgr : public SrvConfigMasterMgr { +public: + /// @brief Statement Tags + /// + /// The contents of the enum are indexes into the list of SQL statements + enum StatementIndex { + // Insert DHCPv4 server configuration. + INSERT_SERVER_CONFIG4, + // Insert DHCPv6 server configuration. + INSERT_SERVER_CONFIG6, + // Retrieve DHCPv4 server configuration filtered by server ID. + GET_CONFIGURATION4_BY_SRV_ID, + // Retrieve DHCPv6 server configuration filtered by server ID. + GET_CONFIGURATION6_BY_SRV_ID, + // Retrieve DHCPv4 server configuration filtered by shard name. + GET_CONFIGURATION4_BY_SHARD_DB, + // Retrieve DHCPv6 server configuration filtered by shard name. + GET_CONFIGURATION6_BY_SHARD_DB, + // Retrieve DHCPv4 synchronization timestamp. + GET_CONFIGURATION4_TIMESTAMP, + // Retrieve DHCPv6 synchronization timestamp. + GET_CONFIGURATION6_TIMESTAMP, + // Delete DHCPv4 server configuration. + DELETE_SERVER_CONFIG4, + // Delete DHCPv6 server configuration. + DELETE_SERVER_CONFIG6, + // Retrieve DHCPv4 shard names. + GET_SERVERS_CONFIG4_SHARDS_NAME, + // Retrieve DHCPv6 shard names. + GET_SERVERS_CONFIG6_SHARDS_NAME, + // Retrieve schema version. + GET_VERSION, + // Number of statements + NUM_STATEMENTS + }; + + /// @brief Constructor + /// + /// @param parameters database connection parameters + explicit PgSqlSrvConfigMasterMgr(const DatabaseConnection::ParameterMap& parameters); + + /// @brief Destructor + virtual ~PgSqlSrvConfigMasterMgr(); + + /// @brief Inserts DHCPv4 server configuration into the master database + /// + /// @param instance_id instance identifier of the server whose configuration is being added + /// @param server_config the configuration being added + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true if server configuration has been added, false otherwise + virtual bool addServerConfig4(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const override; + + /// @brief Inserts DHCPv6 server configuration into the master database + /// + /// @param instance_id instance identifier of the server whose configuration is being added + /// @param server_config the configuration being added + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true if server configuration has been added, false otherwise + virtual bool addServerConfig6(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const override; + + /// @brief Clears all DHCPv4 shard configurations. + /// + /// @return true if shard configurations have been successfully deleted, false otherwise + virtual bool clearServersConfig4() const override; + + /// @brief Clears all DHCPv6 shard configurations. + /// + /// @return true if shard configurations have been successfully deleted, false otherwise + virtual bool clearServersConfig6() const override; + + /// @brief Returns DHCPv4 server configuration information from the database + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one Kea server. + /// + /// @param instance_id the instance identifier of the server being retrieved + /// + /// @return smart pointer to the configuration (or NULL if a configuration is not found) + virtual SrvConfigMasterInfoPtr getConfig4(const std::string& instance_id) const override; + + /// @brief Returns DHCPv6 server configuration information from the database + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one Kea server. + /// + /// @param instance_id the instance identifier of the server being retrieved + /// + /// @return smart pointer to the configuration (or NULL if a configuration is not found) + virtual SrvConfigMasterInfoPtr getConfig6(const std::string& instance_id) const override; + + /// @brief Retrieves configurations of DHCPv4 servers belonging to a given + /// shard. + /// + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations a list of server configurations being retrieved + /// + /// @return true if server configuration has been successfully retrieved, false otherwise + virtual bool getConfig4(const std::string& config_database_name, + std::vector& server_configurations) const override; + + /// @brief Retrieves configurations of DHCPv6 servers belonging to a given + /// shard. + /// + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations a list of server configurations being retrieved + /// + /// @return true if server configuration has been successfully retrieved, false otherwise + virtual bool getConfig6(const std::string& config_database_name, + std::vector& server_configurations) const override; + + /// @brief Retrieves timestamp for a DHCPv4 shard server configuration. + /// + /// There is one timestamp for each shard in the master database. They are + /// updated on every write (including updates) to the master database. + /// + /// @param instance_id the instance identifier of the server for which the timestamp is being + /// retrieved + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigMasterInfoPtr + getMasterConfig4Timestamp(const std::string& instance_id) const override; + + /// @brief Retrieves timestamp for a DHCPv6 shard server configuration. + /// + /// There is one timestamp for each shard in the master database. They are + /// updated on every write (including updates) to the master database. + /// + /// @param instance_id the instance identifier of the server for which the timestamp is being + /// retrieved + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigMasterInfoPtr + getMasterConfig6Timestamp(const std::string& instance_id) const override; + + /// @brief Retrieves all shard names containing DHCPv4 servers from the master database. + /// + /// @return true if shard names have been successfully retrieved, false otherwise + virtual bool getServersConfig4ShardsName(std::set& shards) const override; + + /// @brief Retrieves all shard names containing DHCPv6 servers from the master database. + /// + /// @return true if shard names have been successfully retrieved, false otherwise + virtual bool getServersConfig6ShardsName(std::set& shards) const override; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() override; + + /// @brief Commit Transactions + /// + /// Commits all pending database operations. + /// + /// @throw DbOperationError If the commit failed. + virtual void commit() override; + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. + /// + /// @throw DbOperationError If the rollback failed. + virtual void rollback() override; + + /// @brief Retrieves schema version. + /// + /// @return Version number stored in the database, as a pair of unsigned integers. "first" is + /// the major version number, "second" is the minor version number. + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has failed. + VersionPair getVersion() const override; + + /// @brief Local version of getDBVersion() class method + static std::string getDBVersion(); + + /// @brief Returns the type of backend used, in this case "postgresql". Useful + /// when called from the base class. + virtual std::string getType() const override { + return std::string("postgresql"); + } + +private: + /// @brief Adds server configuration either for DHCPv4 or for DHCPv6. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param instance_id the instance identifier of the server being added to the database + /// @param server_config configuration of the server being added to the database + /// @param config_database configuration to be used on the shard + /// @param config_database_name name of the shard whose configuration is being added + /// + /// @return true, if configuration has been successfully added, false otherwise + bool addCommonServerConfiguration(const StatementIndex statement_index, + const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const; + + /// @brief Retrieves a single server configuration either for DHCPv4 or for + /// DHCPv6 filtered by the instance ID of the server. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param instance_id the instance identifier of the server being retrieved from the database + /// + /// @return the retrieved configuration + SrvConfigMasterInfoPtr + getCommonServerConfigurationByInstanceId(const StatementIndex statement_index, + const std::string& instance_id) const; + + /// @brief Retrieves the configurations of the DHCPv4 or DHCPv6 servers + /// belonging to a given shard. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param config_database_name name of the shard whose configuration is being retrieved + /// @param[out] server_configurations the retrieved configurations + /// + /// @return true if configurations were found, false otherwise + bool getCommonServerConfigurationByShardName( + const StatementIndex statement_index, + const std::string& config_database_name, + std::vector& server_configurations) const; + + /// @brief Retrieves synchronization timestamps from the master database for + /// a given server. + // + /// @param statement_index index of the prepared statement executed in the database + /// @param instance_id the instance identifier of the server by which timestamps are filtered + /// + /// @return a configuration containing the timestamp in question + SrvConfigMasterInfoPtr + getCommonTimestampFromMasterServerConfiguration(const StatementIndex statement_index, + const std::string& instance_id) const; + + /// @brief Retrieves all the shard names from the master database. + /// + /// @param statement_index index of the prepared statement executed in the database + /// @param[out] shards the whole set of shards from the master database + bool getCommonShardNamesFromMasterServerConfiguration(const StatementIndex statement_index, + std::set& shards) const; + + /// @brief Database connection object + mutable PgSqlConnection conn_; + // @brief Exchange in charge of data conversion to and from PostgreSQL types + boost::scoped_ptr exchange_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // PGSQL_SRV_CONFIG_MASTER_MGR_H diff --git a/src/lib/dhcpsrv/pgsql_srv_config_mgr.cc b/src/lib/dhcpsrv/pgsql_srv_config_mgr.cc new file mode 100644 index 0000000000..77f8d3d3f6 --- /dev/null +++ b/src/lib/dhcpsrv/pgsql_srv_config_mgr.cc @@ -0,0 +1,513 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +namespace { + +/// @brief Prepared statements +std::array tagged_statements = { + {// + // GET_VERSION + {0, + {OID_NONE}, + "GET_VERSION", + "SELECT version, minor" + " FROM config_schema_version"}, + + // GET_CONFIGURATION4_TIMESTAMP + {0, + {OID_NONE}, + "GET_CONFIGURATION4_TIMESTAMP", + "SELECT config_id, extract(epoch from timestamp)::bigint" + " FROM server_configuration4 LIMIT 1"}, + + // GET_CONFIGURATION6_TIMESTAMP + {0, + {OID_NONE}, + "GET_CONFIGURATION6_TIMESTAMP", + "SELECT config_id, extract(epoch from timestamp)::bigint" + " FROM server_configuration6 LIMIT 1"}, + + // GET_JSON_CONFIGURATION4 + {0, + {OID_NONE}, + "GET_JSON_CONFIGURATION4", + "SELECT config_id, extract(epoch from timestamp)::bigint, json_data" + " FROM server_configuration4 LIMIT 1"}, + + // GET_JSON_CONFIGURATION6 + {0, + {OID_NONE}, + "GET_JSON_CONFIGURATION6", + "SELECT config_id, extract(epoch from timestamp)::bigint, json_data" + " FROM server_configuration6 LIMIT 1"}, + + // GET_GENERIC_CONFIGURATION4 + {0, + {OID_NONE}, + "GET_GENERIC_CONFIGURATION4", + "SELECT config_id, extract(epoch from timestamp)::bigint, generic_data" + " FROM server_configuration4 LIMIT 1"}, + + // GET_GENERIC_CONFIGURATION6 + {0, + {OID_NONE}, + "GET_GENERIC_CONFIGURATION6", + "SELECT config_id, extract(epoch from timestamp)::bigint, generic_data" + " FROM server_configuration6 LIMIT 1"}, + + // INSERT_CONFIGURATION4 + {4, + {OID_VARCHAR, OID_TIMESTAMP, OID_BYTEA, OID_BYTEA}, + "INSERT_CONFIGURATION4", + "INSERT INTO server_configuration4" + " (config_id, timestamp, json_data, generic_data)" + " VALUES ($1, $2, $3, $4)"}, + + // INSERT_CONFIGURATION6 + {4, + {OID_VARCHAR, OID_TIMESTAMP, OID_BYTEA, OID_BYTEA}, + "INSERT_CONFIGURATION6", + "INSERT INTO server_configuration6" + " (config_id, timestamp, json_data, generic_data)" + " VALUES ($1, $2, $3, $4)"}, + + // UPDATE_CONFIGURATION4 + {4, + {OID_TIMESTAMP, OID_BYTEA, OID_BYTEA, OID_VARCHAR}, + "UPDATE_CONFIGURATION4", + "UPDATE server_configuration4" + " SET timestamp = $1, json_data = $2, generic_data = $3" + " WHERE config_id = $4"}, + + // UPDATE_CONFIGURATION6 + {4, + {OID_TIMESTAMP, OID_BYTEA, OID_BYTEA, OID_VARCHAR}, + "UPDATE_CONFIGURATION6", + "UPDATE server_configuration6" + " SET timestamp = $1, json_data = $2, generic_data = $3" + " WHERE config_id = $4"}}}; + +} // namespace + +PgSqlSrvConfigMgr::PgSqlSrvConfigMgr(const DatabaseConnection::ParameterMap& parameters) + : conn_(parameters) { + + // Open the database. + conn_.openDatabase(); + int i = 0; + for (auto const& it : tagged_statements) { + conn_.prepareStatement(it); + ++i; + } + + // Check that all statements have been prepared. + BOOST_ASSERT(i == NUM_STATEMENTS); +} + +PgSqlSrvConfigMgr::~PgSqlSrvConfigMgr() { +} + +std::string PgSqlSrvConfigMgr::getDBVersion() { + std::stringstream tmp; + tmp << "PostgreSQL backend " << PG_SCHEMA_VERSION_MAJOR; + tmp << "." << PG_SCHEMA_VERSION_MINOR; + tmp << ", library " << PQlibVersion(); + return (tmp.str()); +} + +VersionPair PgSqlSrvConfigMgr::getVersion() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_VERSION); + const StatementIndex stindex = PgSqlSrvConfigMgr::GET_VERSION; + + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name, 0, NULL, NULL, NULL, 0)); + conn_.checkStatementError(r, tagged_statements[stindex]); + + std::istringstream tmp; + uint32_t major; + tmp.str(PQgetvalue(r, 0, 0)); + tmp >> major; + tmp.str(""); + tmp.clear(); + + uint32_t minor; + tmp.str(PQgetvalue(r, 0, 1)); + tmp >> minor; + + return (std::make_pair(major, minor)); +} + +bool PgSqlSrvConfigMgr::updateConfig4(int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG4); + + // Initiate PgSQL transaction as we will have to update the config + // from two steps and we don't want anybody else to write another + // config between these two steps: + // Step 1: Read the current configuration timestamp from database. + // Step 2: Check if the provided configuration timestamp is + // the same with the existing one from database. + // Step 3: Update the configuration on the server. + // If there is no configuration on the server then insert a new one but + // also using transactions because we don't want another user to insert also + // a new configuration in the same time. + + // PgSqlTransaction rolls back the transaction on its destructor. + + // begin the transaction + PgSqlTransaction transaction(conn_); + + SrvConfigInfoPtr current_config = getConfig4Timestamp(); + + if (!current_config) { + if (insertConfig4(json_data, generic_data)) { + transaction.commit(); + return true; + } + return false; + } + + if (!updateConfig4(current_config->config_id_, json_data, generic_data)) { + // The configuration timestamp has been changed since the last + // configuration read until this update. + + LOG_WARN(dhcpsrv_logger, DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED) + .arg(config_timestamp) + .arg(current_config->timestamp_); + + return false; + } + + transaction.commit(); + return true; +} + +bool PgSqlSrvConfigMgr::updateConfig6(int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG6); + + // Initiate PgSQL transaction as we will have to update the config + // from two steps and we don't want anybody else to write another + // config between these two steps: + // Step 1: Read the current configuration timestamp from database. + // Step 2: Check if the provided configuration timestamp is + // the same with the existing one from database. + // Step 3: Update the configuration on the server. + // If there is no configuration on the server then insert a new one but + // also using transactions because we don't want another user to insert also + // a new configuration in the same time. + + // PgSqlTransaction rolls back the transaction on its destructor. + + // begin the transaction + PgSqlTransaction transaction(conn_); + + SrvConfigInfoPtr current_config = getConfig6Timestamp(); + + if (!current_config) { + if (insertConfig6(json_data, generic_data)) { + transaction.commit(); + return true; + } + return false; + } + + if (!updateConfig6(current_config->config_id_, json_data, generic_data)) { + // The configuration timestamp has been changed since the last + // configuration read until this update. + + LOG_WARN(dhcpsrv_logger, DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED) + .arg(config_timestamp) + .arg(current_config->timestamp_); + + return false; + } + + transaction.commit(); + return true; +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getConfig4Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_CONFIG4_TIMESTAMP); + + const StatementIndex stindex = PgSqlSrvConfigMgr::GET_CONFIGURATION4_TIMESTAMP; + return getConfigTimestampCommon(stindex); +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getConfig6Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_CONFIG6_TIMESTAMP); + + const StatementIndex stindex = PgSqlSrvConfigMgr::GET_CONFIGURATION6_TIMESTAMP; + return getConfigTimestampCommon(stindex); +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getConfigTimestampCommon(StatementIndex stindex) const { + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name, 0, NULL, NULL, NULL, 0)); + conn_.checkStatementError(r, tagged_statements[stindex]); + + int affected_rows = boost::lexical_cast(PQcmdTuples(r)); + if (affected_rows < 1) { + return SrvConfigInfoPtr(); + } + + std::istringstream tmp; + std::string config_id; + std::string db_timestamp; + + tmp.str(PQgetvalue(r, 0, 0)); + tmp >> config_id; + tmp.str(""); + tmp.clear(); + + tmp.str(PQgetvalue(r, 0, 1)); + tmp >> db_timestamp; + + time_t timestamp = PgSqlExchange::convertFromDatabaseTime(db_timestamp); + + SrvConfigInfoPtr srvConf = boost::make_shared(); + srvConf->config_id_ = config_id; + srvConf->timestamp_ = timestamp; + + return srvConf; +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getJsonConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_CONFIG4_JSON); + const StatementIndex stindex = PgSqlSrvConfigMgr::GET_JSON_CONFIGURATION4; + SrvConfigInfoPtr result = getConfigCommon(stindex); + result->json_data_ = result->generic_data_; + result->generic_data_ = ""; + return result; +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getJsonConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_CONFIG6_JSON); + const StatementIndex stindex = PgSqlSrvConfigMgr::GET_JSON_CONFIGURATION6; + SrvConfigInfoPtr result = getConfigCommon(stindex); + result->json_data_ = result->generic_data_; + result->generic_data_ = ""; + return result; +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getGenericConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_CONFIG4_GENERIC); + const StatementIndex stindex = PgSqlSrvConfigMgr::GET_GENERIC_CONFIGURATION4; + return getConfigCommon(stindex); +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getGenericConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_SRV_CONFIG6_GENERIC); + const StatementIndex stindex = PgSqlSrvConfigMgr::GET_GENERIC_CONFIGURATION6; + return getConfigCommon(stindex); +} + +SrvConfigInfoPtr PgSqlSrvConfigMgr::getConfigCommon(StatementIndex stindex) const { + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name, 0, NULL, NULL, NULL, 0)); + + conn_.checkStatementError(r, tagged_statements[stindex]); + int affected_rows = boost::lexical_cast(PQcmdTuples(r)); + if (affected_rows < 1) { + return SrvConfigInfoPtr(); + } + + std::istringstream tmp; + std::string config_id; + std::string db_timestamp; + std::string config_data; + + tmp.str(PQgetvalue(r, 0, 0)); + tmp >> config_id; + tmp.str(""); + tmp.clear(); + + tmp.str(PQgetvalue(r, 0, 1)); + tmp >> db_timestamp; + tmp.str(""); + tmp.clear(); + + time_t timestamp = PgSqlExchange::convertFromDatabaseTime(db_timestamp); + + size_t bytes_converted; + unsigned char* bytes = PQunescapeBytea( + reinterpret_cast(PQgetvalue(r, 0, 2)), &bytes_converted); + + if (bytes) { + config_data.assign(bytes, bytes + bytes_converted); + } + PQfreemem(bytes); + + SrvConfigInfoPtr srvConf = boost::make_shared(); + srvConf->config_id_ = config_id; + srvConf->timestamp_ = timestamp; + srvConf->generic_data_ = config_data; + + return srvConf; +} + +bool PgSqlSrvConfigMgr::insertConfig4(const std::string& json_data, + const std::string& generic_data) const { + boost::uuids::random_generator gen; + boost::uuids::uuid unique_id = gen(); + std::string config_id = boost::lexical_cast(unique_id); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_INSERT_SRV_CONFIG4) + .arg(config_id); + + const StatementIndex stindex = PgSqlSrvConfigMgr::INSERT_CONFIGURATION4; + return insertConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool PgSqlSrvConfigMgr::insertConfig6(const std::string& json_data, + const std::string& generic_data) const { + boost::uuids::random_generator gen; + boost::uuids::uuid unique_id = gen(); + std::string config_id = boost::lexical_cast(unique_id); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_INSERT_SRV_CONFIG6) + .arg(config_id); + + const StatementIndex stindex = PgSqlSrvConfigMgr::INSERT_CONFIGURATION6; + return insertConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool PgSqlSrvConfigMgr::insertConfigCommon(StatementIndex stindex, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + PsqlBindArray bind_array; + + std::vector json; + json.assign(json_data.c_str(), json_data.c_str() + json_data.size()); + + std::vector generic; + generic.assign(generic_data.c_str(), generic_data.c_str() + generic_data.size()); + std::string timestamp_str = PgSqlExchange::convertToDatabaseTime(time(NULL)); + + try { + bind_array.add(config_id); + bind_array.add(timestamp_str); + bind_array.add(json); + bind_array.add(generic); + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name, + tagged_statements[stindex].nbparams, &bind_array.values_[0], + &bind_array.lengths_[0], &bind_array.formats_[0], 0)); + + conn_.checkStatementError(r, tagged_statements[stindex]); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not create bind array to insert configuration " + << ", reason: " << ex.what()); + } + return true; +} + +bool PgSqlSrvConfigMgr::updateConfig4(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4) + .arg(config_id); + + const StatementIndex stindex = PgSqlSrvConfigMgr::UPDATE_CONFIGURATION4; + return updateConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool PgSqlSrvConfigMgr::updateConfig6(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6) + .arg(config_id); + + const StatementIndex stindex = PgSqlSrvConfigMgr::UPDATE_CONFIGURATION6; + return updateConfigCommon(stindex, config_id, json_data, generic_data); +} + +bool PgSqlSrvConfigMgr::updateConfigCommon(StatementIndex stindex, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const { + PsqlBindArray bind_array; + + std::vector json; + json.assign(json_data.c_str(), json_data.c_str() + json_data.size()); + + std::vector generic; + generic.assign(generic_data.c_str(), generic_data.c_str() + generic_data.size()); + + std::string timestamp_str = PgSqlExchange::convertToDatabaseTime(time(NULL)); + + try { + bind_array.add(timestamp_str); + bind_array.add(json); + bind_array.add(generic); + bind_array.add(config_id); + PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name, + tagged_statements[stindex].nbparams, &bind_array.values_[0], + &bind_array.lengths_[0], &bind_array.formats_[0], 0)); + + conn_.checkStatementError(r, tagged_statements[stindex]); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, "Could not create bind array to insert configuration " + << ", reason: " << ex.what()); + } + return true; +} + +bool PgSqlSrvConfigMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_BEGIN_TRANSACTION); + PGresult* r = PQexec(conn_, "START TRANSACTION"); + if (PQresultStatus(r) != PGRES_COMMAND_OK) { + const char* error_message = PQerrorMessage(conn_); + PQclear(r); + isc_throw(DbOperationError, "start transaction failed: " << error_message); + } + + PQclear(r); + + return true; +} + +void PgSqlSrvConfigMgr::commit() { + conn_.commit(); +} + +void PgSqlSrvConfigMgr::rollback() { + conn_.rollback(); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/pgsql_srv_config_mgr.h b/src/lib/dhcpsrv/pgsql_srv_config_mgr.h new file mode 100644 index 0000000000..952d095455 --- /dev/null +++ b/src/lib/dhcpsrv/pgsql_srv_config_mgr.h @@ -0,0 +1,300 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PGSQL_SRV_CONFIG_MGR_H +#define PGSQL_SRV_CONFIG_MGR_H + +#include +#include + +namespace isc { +namespace dhcp { + +class PgSqlSrvConfigMgr : public SrvConfigMgr { +public: + /// @brief Constructor + /// + /// Uses the following keywords in the parameters passed to it to + /// connect to the database: + /// - name - Name of the database to which to connect (mandatory) + /// - host - Host to which to connect (optional, defaults to "localhost") + /// - user - Username under which to connect (optional) + /// - password - Password for "user" on the database (optional) + /// + /// If the database is successfully opened, the version number in the + /// schema_version table will be checked against hard-coded value in + /// the implementation file. + /// + /// Finally, all the SQL commands are pre-compiled. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + /// + /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given + /// @throw isc::dhcp::DbOpenError Error opening the database + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + PgSqlSrvConfigMgr(const DatabaseConnection::ParameterMap& parameters); + + /// @brief Destructor (closes database) + virtual ~PgSqlSrvConfigMgr(); + + /// @brief Local version of getDBVersion() class method + static std::string getDBVersion(); + + /// @brief Adds if not exists or updates and existing DHCP V4 server + /// configuration + /// + /// One database contains server configuration data only for one kea server. + /// + /// @param old_config_timestamp If there is already a server configuration in + /// the database + /// then the configuration's timestamp from database is compared with + /// the provided + /// one. The update takes place only if the timestamps are the same. + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @result true if the configuration was added/updated, false if not. + virtual bool updateConfig4(int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Adds if not exists or updates and existing DHCP V6 server + /// configuration + /// + /// One database contains server configuration data only for one kea server. + /// + /// @param old_config_timestamp If there is already a server configuration in + /// the database then the configuration's timestamp from database is + /// compared with the provided one. The update takes place only if the + /// timestamps are the same. + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added/updated, false if not. + virtual bool updateConfig6(int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Retrieves timestamp for the DHCPv4 shard server configuration. + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigInfoPtr getConfig4Timestamp() const; + + /// @brief Retrieves timestamp for the DHCPv6 shard server configuration. + /// + /// @return pointer to the generic structure containing the timestamp + virtual SrvConfigInfoPtr getConfig6Timestamp() const; + + /// @brief Retrieves the configuration of the DHCPv4 server belonging to the + /// current shard in JSON format. + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getJsonConfig4() const; + + /// @brief Retrieves the configuration of the DHCPv6 server belonging to the + /// current shard in JSON format. + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getJsonConfig6() const; + + /// @brief Retrieves the configuration of the DHCPv4 server belonging to the + /// current shard in generic format (e.g. YAML). + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getGenericConfig4() const; + + /// @brief Retrieves the configuration of the DHCPv6 server belonging to the + /// current shard in generic format (e.g. YAML). + /// + /// @return server configuration being retrieved + virtual SrvConfigInfoPtr getGenericConfig6() const; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction(); + + /// @brief Commit Transactions + /// + /// Commits all pending database operations. + /// + /// @throw DbOperationError If the commit failed. + virtual void commit(); + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. + /// + /// @throw DbOperationError If the rollback failed. + virtual void rollback(); + + /// @brief Returns backend version. + /// + /// @return Version number as a pair of unsigned integers. "first" is the + /// major version number, "second" the minor number. + /// + /// @throw isc::dhcp::DbOperationError An operation on the open database has + /// failed. + virtual VersionPair getVersion() const; + + /// @brief Return backend type + /// + /// @return the type of the backend ('postgresql'). + virtual std::string getType() const { + return (std::string("postgresql")); + } + + /// @brief Statement Tags + /// + /// The contents of the enum are indexes into the list of SQL statements + enum StatementIndex { + GET_VERSION, // Obtain version number + GET_CONFIGURATION4_TIMESTAMP, // Get the timestamp of the v4 stored + // configuration + GET_CONFIGURATION6_TIMESTAMP, // Get the timestamp of the v6 stored + // configuration + GET_JSON_CONFIGURATION4, // Get only JSON server configuration v4 + GET_JSON_CONFIGURATION6, // Get only JSON server configuration v6 + GET_GENERIC_CONFIGURATION4, // Get only GENERIC server configuration v4 + GET_GENERIC_CONFIGURATION6, // Get only GENERIC server configuration v6 + INSERT_CONFIGURATION4, // Add server configuration v4 + INSERT_CONFIGURATION6, // Add server configuration v6 + UPDATE_CONFIGURATION4, // Update server configuration v4 + UPDATE_CONFIGURATION6, // Update server configuration v6 + NUM_STATEMENTS // Number of statements + }; + +private: + /// @brief Inserts a new DHCPv4 server configuration. + /// + /// + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added, false if not. + bool insertConfig4(const std::string& json_data, const std::string& generic_data) const; + + /// @brief Inserts a new DHCPv6 server configuration. + /// + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added, false if not. + bool insertConfig6(const std::string& json_data, const std::string& generic_data) const; + + /// @brief Updates an existing DHCPv4 server configuration. + /// + /// @param config_id todo + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added/updated, false if not. + bool updateConfig4(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Updates an existing DHCPv6 server configuration. + /// + /// @param config_id todo + /// @param json_data The server configuration to be written in json format + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @throw isc::dhcp::DbOperationError Connection is ok, so it must be an + /// SQL error. + /// + /// @return true if the configuration was added/updated, false if not. + bool updateConfig6(const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Retrieves synchronization timestamps from the shard database. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// + /// @return a configuration containing the timestamp in question + SrvConfigInfoPtr getConfigTimestampCommon(const StatementIndex statement_index) const; + + /// @brief Retrieves a single server configuration either for DHCPv4 or for + /// DHCPv6 from the current shard. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// + /// @return the single retrieved configuration + SrvConfigInfoPtr getConfigCommon(const StatementIndex statement_index) const; + + /// @brief Adds server configuration either for DHCPv4 or for DHCPv6. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// @param config_id unique ID for configuration + /// @param json_data JSON configuration being inserted + /// @param generic_data generic configuration (e.g. YAML) being inserted + /// + /// @return true, if configuration has been successfully added, false + /// otherwise + bool insertConfigCommon(const StatementIndex statement_index, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief Updates server configuration either for DHCPv4 or for DHCPv6. + /// + /// @param statement_index index of the prepared statement being executed in + /// the database + /// @param config_id unique ID for configuration + /// @param json_data JSON configuration to update the current configuration + /// @param generic_data generic configuration (e.g. YAML) to update the + /// current configuration + /// + /// @return true, if configuration has been successfully updated, false + /// otherwise + bool updateConfigCommon(const StatementIndex statement_index, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data) const; + + /// @brief PostgreSQL connection handle + mutable PgSqlConnection conn_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // PGSQL_SRV_CONFIG_MGR_H diff --git a/src/lib/dhcpsrv/shared_network.cc b/src/lib/dhcpsrv/shared_network.cc index 695a9cf5ff..7dc60603b2 100644 --- a/src/lib/dhcpsrv/shared_network.cc +++ b/src/lib/dhcpsrv/shared_network.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/lib/dhcpsrv/sql_common.h b/src/lib/dhcpsrv/sql_common.h index 610abd62e1..ba0acdb583 100644 --- a/src/lib/dhcpsrv/sql_common.h +++ b/src/lib/dhcpsrv/sql_common.h @@ -1,6 +1,6 @@ -// Copyright (C) 2016-2017 Deutsche Telekom AG. +// Copyright (C) 2016-2018 Deutsche Telekom AG. // -// Author: Cristian Secăreanu +// Author: Andrei Pavel // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,9 @@ namespace isc { namespace dhcp { +/// @brief Pair containing major and minor versions +typedef std::pair VersionPair; + /// @brief Used to map server data types with internal backend storage data /// types. enum ExchangeDataType { diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 3e29985b3f..5e879b145d 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -5,15 +5,19 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include + +#include // Needed for HWADDR_SOURCE_* #include #include #include #include #include #include -#include // Needed for HWADDR_SOURCE_* + +#include #include #include +#include using namespace isc::log; using namespace isc::data; @@ -22,7 +26,7 @@ namespace isc { namespace dhcp { SrvConfig::SrvConfig() - : sequence_(0), cfg_iface_(new CfgIface()), + : instance_id_(""), sequence_(0), cfg_iface_(new CfgIface()), cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()), cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()), cfg_shared_networks4_(new CfgSharedNetworks4()), @@ -34,11 +38,13 @@ SrvConfig::SrvConfig() cfg_host_operations6_(CfgHostOperations::createConfig6()), class_dictionary_(new ClientClassDictionary()), decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0), + master_server_cfg_timestamp_(-1), + server_cfg_timestamp_(-1), d2_client_config_(new D2ClientConfig()) { } SrvConfig::SrvConfig(const uint32_t sequence) - : sequence_(sequence), cfg_iface_(new CfgIface()), + : instance_id_(""), sequence_(sequence), cfg_iface_(new CfgIface()), cfg_option_def_(new CfgOptionDef()), cfg_option_(new CfgOption()), cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()), cfg_shared_networks4_(new CfgSharedNetworks4()), @@ -50,11 +56,12 @@ SrvConfig::SrvConfig(const uint32_t sequence) cfg_host_operations6_(CfgHostOperations::createConfig6()), class_dictionary_(new ClientClassDictionary()), decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0), + master_server_cfg_timestamp_(-1), + server_cfg_timestamp_(-1), d2_client_config_(new D2ClientConfig()) { } -std::string -SrvConfig::getConfigSummary(const uint32_t selection) const { +std::string SrvConfig::getConfigSummary(const uint32_t selection) const { std::ostringstream s; size_t subnets_num; if ((selection & CFGSEL_SUBNET4) == CFGSEL_SUBNET4) { @@ -94,13 +101,11 @@ SrvConfig::getConfigSummary(const uint32_t selection) const { return (summary); } -bool -SrvConfig::sequenceEquals(const SrvConfig& other) { +bool SrvConfig::sequenceEquals(const SrvConfig& other) { return (getSequence() == other.getSequence()); } -void -SrvConfig::copy(SrvConfig& new_config) const { +void SrvConfig::copy(SrvConfig& new_config) const { // We will entirely replace loggers in the new configuration. new_config.logging_info_.clear(); for (LoggingInfoStorage::const_iterator it = logging_info_.begin(); @@ -126,9 +131,7 @@ SrvConfig::copy(SrvConfig& new_config) const { } } -void -SrvConfig::applyLoggingCfg() const { - +void SrvConfig::applyLoggingCfg() const { std::list specs; for (LoggingInfoStorage::const_iterator it = logging_info_.begin(); it != logging_info_.end(); ++it) { @@ -138,8 +141,7 @@ SrvConfig::applyLoggingCfg() const { manager.process(specs.begin(), specs.end()); } -bool -SrvConfig::equals(const SrvConfig& other) const { +bool SrvConfig::equals(const SrvConfig& other) const { // If number of loggers is different, then configurations aren't equal. if (logging_info_.size() != other.logging_info_.size()) { return (false); @@ -147,9 +149,8 @@ SrvConfig::equals(const SrvConfig& other) const { // Pass through all loggers and try to find the match for each of them // with the loggers from the other configuration. The order doesn't // matter so we can't simply compare the vectors. - for (LoggingInfoStorage::const_iterator this_it = - logging_info_.begin(); this_it != logging_info_.end(); - ++this_it) { + for (LoggingInfoStorage::const_iterator this_it = logging_info_.begin(); + this_it != logging_info_.end(); ++this_it) { bool match = false; for (LoggingInfoStorage::const_iterator other_it = other.logging_info_.begin(); @@ -182,9 +183,7 @@ SrvConfig::equals(const SrvConfig& other) const { return (hooks_config_.equal(other.hooks_config_)); } -void -SrvConfig::removeStatistics() { - +void SrvConfig::removeStatistics() { // Removes statistics for v4 and v6 subnets getCfgSubnets4()->removeStatistics(); diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h index 5ca75aefbc..a9d464728f 100644 --- a/src/lib/dhcpsrv/srv_config.h +++ b/src/lib/dhcpsrv/srv_config.h @@ -14,13 +14,14 @@ #include #include #include +#include #include #include #include +#include #include #include #include -#include #include #include #include @@ -28,43 +29,45 @@ #include #include #include -#include + #include +#include + namespace isc { namespace dhcp { class CfgMgr; - /// @brief Specifies current DHCP configuration /// /// @todo Migrate all other configuration parameters from cfgmgr.h here class SrvConfig : public UserContext, public isc::data::CfgToElement { public: - /// @name Constants for selection of parameters returned by @c getConfigSummary + /// @name Constants for selection of parameters returned by @c + /// getConfigSummary /// //@{ /// Nothing selected - static const uint32_t CFGSEL_NONE = 0x00000000; + static const uint32_t CFGSEL_NONE = 0x00000000; /// Number of IPv4 subnets static const uint32_t CFGSEL_SUBNET4 = 0x00000001; /// Number of IPv6 subnets static const uint32_t CFGSEL_SUBNET6 = 0x00000002; /// Number of enabled ifaces - static const uint32_t CFGSEL_IFACE4 = 0x00000004; + static const uint32_t CFGSEL_IFACE4 = 0x00000004; /// Number of v6 ifaces - static const uint32_t CFGSEL_IFACE6 = 0x00000008; + static const uint32_t CFGSEL_IFACE6 = 0x00000008; /// DDNS enabled/disabled - static const uint32_t CFGSEL_DDNS = 0x00000010; + static const uint32_t CFGSEL_DDNS = 0x00000010; /// Number of all subnets - static const uint32_t CFGSEL_SUBNET = 0x00000003; + static const uint32_t CFGSEL_SUBNET = 0x00000003; /// IPv4 related config - static const uint32_t CFGSEL_ALL4 = 0x00000015; + static const uint32_t CFGSEL_ALL4 = 0x00000015; /// IPv6 related config - static const uint32_t CFGSEL_ALL6 = 0x0000001A; + static const uint32_t CFGSEL_ALL6 = 0x0000001A; /// Whole config - static const uint32_t CFGSEL_ALL = 0xFFFFFFFF; + static const uint32_t CFGSEL_ALL = 0xFFFFFFFF; //@} /// @brief Default constructor. @@ -77,6 +80,26 @@ class SrvConfig : public UserContext, public isc::data::CfgToElement { /// Sets arbitrary configuration sequence number. SrvConfig(const uint32_t sequence); + /// @brief Returns a const reference to the unique instance identifier. + /// + /// This method returns a const reference to a instance identifier + /// unique among running servers. + /// It is used to uniquely identify a server in a master database. + /// The master database contains mappings of servers to shards. + const std::string& getInstanceId() const { + return instance_id_; + } + + /// @brief Returns the unique instance identifier. + /// + /// This method returns a reference to a instance identifier + /// unique among running servers. + /// It is used to uniquely identify a server in a master database. + /// The master database contains mappings of servers to shards. + std::string& getInstanceId() { + return instance_id_; + } + /// @brief Returns summary of the configuration in the textual format. /// /// This method returns the brief text describing the current configuration. @@ -303,6 +326,20 @@ class SrvConfig : public UserContext, public isc::data::CfgToElement { return (cfg_duid_); } + /// @brief Returns non-const reference to configuration type information. + /// + /// @return non-const reference to configuration type + CfgSrvConfigType& getConfigurationType() { + return (cfg_configuration_type_); + } + + /// @brief Returns const reference to configuration type information. + /// + /// @return const reference to configuration type + const CfgSrvConfigType& getConfigurationType() const { + return (cfg_configuration_type_); + } + /// @brief Returns pointer to the object holding configuration of the /// lease and host database connection parameters. CfgDbAccessPtr getCfgDbAccess() { @@ -540,6 +577,34 @@ class SrvConfig : public UserContext, public isc::data::CfgToElement { return (dhcp4o6_port_); } + /// @brief Sets the master server's configuration timestamp. + /// + /// @param timestamp value of the master server's configuration timestamp + void setMasterServerCfgTimestamp(int64_t timestamp) { + master_server_cfg_timestamp_ = timestamp; + } + + /// @brief Retrieves the master server's configuration timestamp. + /// + /// @return value of the master server's configuration timestamp + int64_t getMasterServerCfgTimestamp() const { + return (master_server_cfg_timestamp_); + } + + /// @brief Sets the shard's configuration timestamp. + /// + /// @param timestamp value of the shard's configuration timestamp + void setServerCfgTimestamp(int64_t timestamp) { + server_cfg_timestamp_ = timestamp; + } + + /// @brief Retrieves the shard's configuration timestamp. + /// + /// @return value of the shard's configuration timestamp + int64_t getServerCfgTimestamp() const { + return (server_cfg_timestamp_); + } + /// @brief Returns pointer to the D2 client configuration D2ClientConfigPtr getD2ClientConfig() { return (d2_client_config_); @@ -562,6 +627,8 @@ class SrvConfig : public UserContext, public isc::data::CfgToElement { virtual isc::data::ElementPtr toElement() const; private: + /// @brief Instance identifier unique among running servers. + std::string instance_id_; /// @brief Sequence number identifying the configuration. uint32_t sequence_; @@ -621,6 +688,9 @@ class SrvConfig : public UserContext, public isc::data::CfgToElement { /// @brief Pointer to the configuration of the server identifier. CfgDUIDPtr cfg_duid_; + /// @brief Configuration might be read from a local file or from database + CfgSrvConfigType cfg_configuration_type_; + /// @brief Pointer to the configuration of the lease and host database /// connection parameters. CfgDbAccessPtr cfg_db_access_; @@ -657,6 +727,12 @@ class SrvConfig : public UserContext, public isc::data::CfgToElement { /// this socket is bound and connected to this port and port + 1 uint16_t dhcp4o6_port_; + /// @brief The server configuration version of on the master dabatase + int64_t master_server_cfg_timestamp_; + + /// @brief The configuration timestamp of the server configuration. + int64_t server_cfg_timestamp_; + D2ClientConfigPtr d2_client_config_; }; @@ -670,7 +746,7 @@ typedef boost::shared_ptr SrvConfigPtr; typedef boost::shared_ptr ConstSrvConfigPtr; //@} -} // namespace isc::dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // DHCPSRV_CONFIG_H diff --git a/src/lib/dhcpsrv/srv_config_master_mgr.cc b/src/lib/dhcpsrv/srv_config_master_mgr.cc new file mode 100644 index 0000000000..ba1f95e541 --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_master_mgr.cc @@ -0,0 +1,39 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +using namespace std; + +namespace isc { +namespace dhcp { + +SrvConfigMasterMgr::SrvConfigMasterMgr() { +} + +SrvConfigMasterMgr::~SrvConfigMasterMgr() { +} + +std::string SrvConfigMasterMgr::getDBVersion() { + isc_throw(NotImplemented, "SrvConfigMasterMgr::getDBVersion() called"); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/srv_config_master_mgr.h b/src/lib/dhcpsrv/srv_config_master_mgr.h new file mode 100644 index 0000000000..83ab6b292e --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_master_mgr.h @@ -0,0 +1,175 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRV_CONFIG_MASTER_MGR_H +#define SRV_CONFIG_MASTER_MGR_H + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Holds server configuration data from the master database. +/// +/// Holds server configuration data from the master database. +/// +struct SrvConfigMasterInfo { + std::string instance_id_; + int64_t timestamp_; + std::string server_config_; + std::string config_database_; + std::string config_database_name_; +}; + +/// @brief Shared pointer to server configuration string. +typedef boost::shared_ptr SrvConfigMasterInfoPtr; +typedef std::vector SrvConfigMasterInfoCollection; + +/// @brief Abstract Server Configuration Manager +/// +/// This is an abstract API for server configuration database backends. +/// It provides unified interface to all backends. As this is an abstract class, +/// it should not be used directly, but rather specialized derived class should +/// be used instead. +/// +/// This class throws no exceptions. However, methods in concrete +/// implementations of this class may throw exceptions: see the documentation +/// of those classes for details. +class SrvConfigMasterMgr { +public: + /// @brief Constructor + /// + SrvConfigMasterMgr(); + + /// @brief Destructor + virtual ~SrvConfigMasterMgr(); + + /// @brief Class method to return extended version info + /// This class method must be redeclared and redefined in derived classes + static std::string getDBVersion(); + + virtual bool clearServersConfig4() const = 0; + + virtual bool clearServersConfig6() const = 0; + + virtual bool addServerConfig4(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const = 0; + + virtual bool addServerConfig6(const std::string& instance_id, + const std::string& server_config, + const std::string& config_database, + const std::string& config_database_name) const = 0; + + /// @brief Returns DHCPv4 instance configuration information + /// + /// This method returns the instance configuration. + /// + /// @return smart pointer to the configuration (or NULL if a configuration + /// is not found) + virtual SrvConfigMasterInfoPtr + getConfig4(const std::string& instance_id) const = 0; + + /// @brief Returns DHCPv6 instance configuration information + /// + /// This method returns the instance configuration. + /// + /// @return smart pointer to the configuration (or NULL if a configuration + /// is not found) + virtual SrvConfigMasterInfoPtr + getConfig6(const std::string& instance_id) const = 0; + + virtual bool + getConfig4(const std::string& config_database_name, + std::vector& serverInfo) const = 0; + + virtual bool + getConfig6(const std::string& config_database_name, + std::vector& serverInfo) const = 0; + + virtual SrvConfigMasterInfoPtr + getMasterConfig4Timestamp(const std::string& instance_id) const = 0; + + virtual SrvConfigMasterInfoPtr + getMasterConfig6Timestamp(const std::string& instance_id) const = 0; + + virtual bool + getServersConfig4ShardsName(std::set& shards_list) const = 0; + + virtual bool + getServersConfig6ShardsName(std::set& shards_list) const = 0; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() = 0; + + /// @brief Commit Transactions + /// + /// Commits all pending database operations. On databases that don't + /// support transactions, this is a no-op. + virtual void commit() = 0; + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. On databases that don't + /// support transactions, this is a no-op. + virtual void rollback() = 0; + + /// @brief Returns backend version. + /// + /// @return Version number as a pair of unsigned integers. "first" is the + /// major version number, "second" the minor number. + /// + /// @todo: We will need to implement 3 version functions eventually: + /// A. abstract API version + /// B. backend version + /// C. database version (stored in the database scheme) + /// + /// and then check that: + /// B>=A and B=C (it is ok to have newer backend, as it should be backward + /// compatible) + /// Also if B>C, some database upgrade procedure may be triggered + virtual VersionPair getVersion() const = 0; + + /// @brief Return backend type + /// + /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) + /// + /// @return Type of the backend. + virtual std::string getType() const = 0; +}; + +} // namespace dhcp +} // namespace isc + +#endif // SRV_CONFIG_MASTER_MGR_H diff --git a/src/lib/dhcpsrv/srv_config_master_mgr_factory.cc b/src/lib/dhcpsrv/srv_config_master_mgr_factory.cc new file mode 100644 index 0000000000..bb0fad5337 --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_master_mgr_factory.cc @@ -0,0 +1,116 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +#ifdef HAVE_MYSQL +#include +#endif +#ifdef HAVE_PGSQL +#include +#endif +#ifdef HAVE_CQL +#include +#endif + +#include +#include + +namespace isc { +namespace dhcp { + +boost::scoped_ptr& +SrvConfigMasterMgrFactory::getConfigurationMgrPtr() { + static boost::scoped_ptr configurationMgrPtr; + return (configurationMgrPtr); +} + +void +SrvConfigMasterMgrFactory::create(const std::string& dbaccess) { + const std::string type = "type"; + + // Parse the access string and create a redacted string for logging. + DatabaseConnection::ParameterMap parameters = + DatabaseConnection::parse(dbaccess); + std::string redacted = DatabaseConnection::redactedAccessString(parameters); + + // Is "type" present? + if (parameters.find(type) == parameters.end()) { + LOG_ERROR(dhcpsrv_logger, DHCPSRV_NOTYPE_DB).arg(dbaccess); + isc_throw(InvalidParameter, "Database configuration parameters do not " + "contain the 'type' keyword"); + } + + // Yes, check what it is. +#ifdef HAVE_MYSQL + if (parameters[type] == std::string("mysql")) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_MYSQL_DB).arg(redacted); + getConfigurationMgrPtr().reset(new MySqlSrvConfigMasterMgr(parameters)); + return; + } +#endif +#ifdef HAVE_PGSQL + if (parameters[type] == std::string("postgresql")) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_DB).arg(redacted); + getConfigurationMgrPtr().reset(new PgSqlSrvConfigMasterMgr(parameters)); + return; + } +#endif +#ifdef HAVE_CQL + if (parameters[type] == std::string("cql")) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_CQL_DB).arg(redacted); + getConfigurationMgrPtr().reset(new CqlSrvConfigMasterMgr(parameters)); + return; + } +#endif + + // Get here on no match + LOG_ERROR(dhcpsrv_logger, DHCPSRV_UNKNOWN_DB).arg(parameters[type]); + isc_throw(InvalidType, "Database access parameter 'type' does " + "not specify a supported database backend"); +} + +void +SrvConfigMasterMgrFactory::destroy() { + // Destroy current lease manager. This is a no-op if no lease manager + // is available. + if (getConfigurationMgrPtr()) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CLOSE_DB) + .arg(getConfigurationMgrPtr()->getType()); + } + getConfigurationMgrPtr().reset(); +} + +SrvConfigMasterMgr& +SrvConfigMasterMgrFactory::instance() { + SrvConfigMasterMgr* cfgptr = getConfigurationMgrPtr().get(); + if (cfgptr == NULL) { + isc_throw(NoServerConfigMasterManager, + "no current server configuration manager is available"); + } + return (*cfgptr); +} + +bool +SrvConfigMasterMgrFactory::initialized() { + SrvConfigMasterMgr* cfgptr = getConfigurationMgrPtr().get(); + return (cfgptr != NULL); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/srv_config_master_mgr_factory.h b/src/lib/dhcpsrv/srv_config_master_mgr_factory.h new file mode 100644 index 0000000000..011eca1c41 --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_master_mgr_factory.h @@ -0,0 +1,111 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRV_CONFIG_MASTER_MGR_FACTORY_H +#define SRV_CONFIG_MASTER_MGR_FACTORY_H + +#include +#include +#include + +#include + +#include + +namespace isc { +namespace dhcp { + +/// @brief No configuration manager exception +/// +/// Thrown if an attempt is made to get a reference to the current configuration +/// manager and none is currently available. +class NoServerConfigMasterManager : public Exception { +public: + NoServerConfigMasterManager(const char* file, size_t line, const char* what) + : isc::Exception(file, line, what) { + } +}; + +/// @brief Server Configuration Manager Factory +/// +/// This class comprises nothing but static methods used to create a server +/// configuration manager. It analyzes the database information passed to the +/// creation function and instantiates an appropriate server configuration +/// manager based on the type requested. +/// +/// Strictly speaking these functions could be stand-alone functions. However, +/// it is convenient to encapsulate them in a class for naming purposes. +/// +/// @todo: Will need to develop some form of registration mechanism for +/// user-supplied backends (so that there is no need to modify the code). +class SrvConfigMasterMgrFactory { +public: + /// @brief Create an instance of a server configuration manager. + /// + /// Each database backend has its own server configuration manager type. + /// This static method sets the "current" server configuration manager + /// to be a manager of the appropriate type. The actual server + /// configuration is returned by the "instance" method. + /// + /// @note When called, the current server configuration manager is + /// always destroyed and a new one created - even if + /// the parameters are the same. + /// + /// dbaccess is a generic way of passing parameters. Parameters are passed + /// in the "name=value" format, separated by spaces. The data MUST include + /// a keyword/value pair of the form "type=dbtype" giving the database + /// type, e.q. "mysql" or "sqlite3". + /// + /// @param dbaccess Database access parameters. These are in the form of + /// "keyword=value" pairs, separated by spaces. They are backend- + /// -end specific, although must include the "type" keyword which + /// gives the backend in use. + /// + /// @throw isc::InvalidParameter dbaccess string does not contain the "type" + /// keyword. + /// @throw isc::dhcp::InvalidType The "type" keyword in dbaccess does not + /// identify a supported backend. + static void create(const std::string& dbaccess); + + /// @brief Destroy server configuration manager + /// + /// Destroys the current server configuration manager object. This should + /// have the effect of closing the database connection. The method is a + /// no-op if no server configuration manager is available. + static void destroy(); + + /// @brief Return current server configuration manager + /// + /// Returns an instance of the "current" server configuration manager. + /// An exception will be thrown if none is available. + /// + /// @throw isc::dhcp::NoLeaseManager No lease manager is available: use + /// create() to create one before calling this method. + static SrvConfigMasterMgr& instance(); + + static bool initialized(); + +private: + /// @brief Hold pointer to server configuration manager + /// + /// Holds a pointer to the singleton server configuration manager. + /// The singleton is encapsulated in this method to avoid a "static + /// initialization fiasco" if defined in an external static variable. + static boost::scoped_ptr& getConfigurationMgrPtr(); +}; + +} // namespace dhcp +} // namespace isc + +#endif // SRV_CONFIG_MASTER_MGR_FACTORY_H diff --git a/src/lib/dhcpsrv/srv_config_mgr.cc b/src/lib/dhcpsrv/srv_config_mgr.cc new file mode 100644 index 0000000000..db0ec9a925 --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_mgr.cc @@ -0,0 +1,39 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include + +using namespace std; + +namespace isc { +namespace dhcp { + +SrvConfigMgr::SrvConfigMgr() { +} + +SrvConfigMgr::~SrvConfigMgr() { +} + +std::string SrvConfigMgr::getDBVersion() { + isc_throw(NotImplemented, "SrvConfigMgr::getDBVersion() called"); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/srv_config_mgr.h b/src/lib/dhcpsrv/srv_config_mgr.h new file mode 100644 index 0000000000..a05d0f0050 --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_mgr.h @@ -0,0 +1,190 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRV_CONFIG_MGR_H +#define SRV_CONFIG_MGR_H + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Holds server configuration data +/// +/// Holds server configuration data in order to read/write the configuration +/// from/to the database. +/// +struct SrvConfigInfo { + std::string config_id_; + int64_t timestamp_; + std::string json_data_; + std::string generic_data_; +}; + +/// @brief Shared pointer to server configuration string. +typedef boost::shared_ptr SrvConfigInfoPtr; +typedef std::vector SrvConfigInfoCollection; + +/// @brief Abstract Server Configuration Manager +/// +/// This is an abstract API for server configuration database backends. +/// It provides unified interface to all backends. As this is an abstract class, +/// it should not be used directly, but rather specialized derived class should +/// be used +/// instead. +/// +/// This class throws no exceptions. However, methods in concrete +/// implementations of this class may throw exceptions: see the documentation +/// of those classes for details. +class SrvConfigMgr { +public: + /// @brief Constructor + /// + SrvConfigMgr(); + + /// @brief Destructor + virtual ~SrvConfigMgr(); + + /// @brief Class method to return extended version info + /// This class method must be redeclared and redefined in derived classes + static std::string getDBVersion(); + + /// @brief Adds if not exists or updates and existing DHCP V4 server + /// configuration + /// + /// One database contains server configuration data only for one kea server. + /// + /// @param old_config_timestamp If there is already a server configuration + /// in the database + /// then the configuration's timestamp from database is compared with + /// the provided + /// one. The update takes place only if the timestamps are the same. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @result true if the configuration was added/updated, false if not. + virtual bool updateConfig4(int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const = 0; + + /// @brief Adds if not exists or updates and existing DHCP V6 server + /// configuration + /// + /// One database contains server configuration data only for one kea server. + /// + /// @param old_config_timestamp If there is already a server configuration + /// in the database + /// then the configuration's timestamp from database is compared with + /// the provided + /// one. The update takes place only if the timestamps are the same. + /// + /// @param json_data The server configuration to be written in json format + /// + /// @param generic_data The server configuration to be written in GENERIC + /// format + /// + /// @result true if the configuration was added/updated, false if not. + virtual bool updateConfig6(int64_t old_config_timestamp, + const std::string& json_data, + const std::string& generic_data) const = 0; + + virtual SrvConfigInfoPtr getConfig4Timestamp() const = 0; + + virtual SrvConfigInfoPtr getConfig6Timestamp() const = 0; + + /// @brief Returns server configuration information + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one kea server. + /// + /// @return smart pointer to the configuration (or NULL if a configuration + /// is not found) + virtual SrvConfigInfoPtr getJsonConfig4() const = 0; + + virtual SrvConfigInfoPtr getGenericConfig4() const = 0; + + /// @brief Returns server configuration information + /// + /// This method returns the server configuration. + /// One database contains server configuration data only for one kea server. + /// + /// @return smart pointer to the configuration (or NULL if a configuration + /// is not found) + virtual SrvConfigInfoPtr getJsonConfig6() const = 0; + + virtual SrvConfigInfoPtr getGenericConfig6() const = 0; + + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() = 0; + + /// @brief Commit Transactions + /// + /// Commits all pending database operations. On databases that don't + /// support transactions, this is a no-op. + virtual void commit() = 0; + + /// @brief Rollback Transactions + /// + /// Rolls back all pending database operations. On databases that don't + /// support transactions, this is a no-op. + virtual void rollback() = 0; + + /// @brief Returns backend version. + /// + /// @return Version number as a pair of unsigned integers. "first" is the + /// major version number, "second" the minor number. + /// + /// If we ever have 3 version functions: + /// A. abstract API version + /// B. backend version + /// C. database version (stored in the database scheme) + /// we could check that: + /// B>=A and B=C (it is ok to have newer backend, as it should be backward + /// compatible) + /// Also if B>C, some database upgrade procedure may be triggered + virtual VersionPair getVersion() const = 0; + + /// @brief Return backend type + /// + /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) + /// + /// @return Type of the backend. + virtual std::string getType() const = 0; +}; + +} // namespace dhcp +} // namespace isc + +#endif // SRV_CONFIG_MGR_H diff --git a/src/lib/dhcpsrv/srv_config_mgr_factory.cc b/src/lib/dhcpsrv/srv_config_mgr_factory.cc new file mode 100644 index 0000000000..5825e1af8f --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_mgr_factory.cc @@ -0,0 +1,126 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include + +#ifdef HAVE_MYSQL +#include +#endif +#ifdef HAVE_PGSQL +#include +#endif +#ifdef HAVE_CQL +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace isc { +namespace dhcp { + +boost::scoped_ptr& +SrvConfigMgrFactory::getConfigurationMgrPtr() { + static boost::scoped_ptr configurationMgrPtr; + return (configurationMgrPtr); +} + +void +SrvConfigMgrFactory::create(const std::string& dbaccess) { + const std::string type = "type"; + + // Parse the access string and create a redacted string for logging. + DatabaseConnection::ParameterMap parameters = + DatabaseConnection::parse(dbaccess); + std::string redacted = DatabaseConnection::redactedAccessString(parameters); + + // Is "type" present? + if (parameters.find(type) == parameters.end()) { + LOG_ERROR(dhcpsrv_logger, DHCPSRV_NOTYPE_DB).arg(dbaccess); + isc_throw(InvalidParameter, "Database configuration parameters do not " + "contain the 'type' keyword"); + } + +// Yes, check what it is. +#ifdef HAVE_MYSQL + if (parameters[type] == string("mysql")) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_MYSQL_DB).arg(redacted); + getConfigurationMgrPtr().reset(new MySqlSrvConfigMgr(parameters)); + return; + } +#endif +#ifdef HAVE_PGSQL + if (parameters[type] == string("postgresql")) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_DB).arg(redacted); + getConfigurationMgrPtr().reset(new PgSqlSrvConfigMgr(parameters)); + return; + } +#endif +#ifdef HAVE_CQL + if (parameters[type] == string("cql")) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_CQL_DB).arg(redacted); + getConfigurationMgrPtr().reset(new CqlSrvConfigMgr(parameters)); + return; + } +#endif + + // Get here on no match + LOG_ERROR(dhcpsrv_logger, DHCPSRV_UNKNOWN_DB).arg(parameters[type]); + isc_throw(InvalidType, "Database access parameter 'type' does " + "not specify a supported database backend"); +} + +void +SrvConfigMgrFactory::destroy() { + // Destroy current lease manager. This is a no-op if no lease manager + // is available. + if (getConfigurationMgrPtr()) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CLOSE_DB) + .arg(getConfigurationMgrPtr()->getType()); + } + getConfigurationMgrPtr().reset(); +} + +SrvConfigMgr& +SrvConfigMgrFactory::instance() { + SrvConfigMgr* cfgptr = getConfigurationMgrPtr().get(); + if (cfgptr == NULL) { + isc_throw(NoServerConfigManager, + "no current server configuration manager is available"); + } + return (*cfgptr); +} + +bool +SrvConfigMgrFactory::initialized() { + SrvConfigMgr* cfgptr = getConfigurationMgrPtr().get(); + return (cfgptr != NULL); +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/srv_config_mgr_factory.h b/src/lib/dhcpsrv/srv_config_mgr_factory.h new file mode 100644 index 0000000000..8109106747 --- /dev/null +++ b/src/lib/dhcpsrv/srv_config_mgr_factory.h @@ -0,0 +1,117 @@ +// Copyright (C) 2016-2018 Deutsche Telekom AG. +// +// Author: Cristian Secareanu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRV_CONFIG_MGR_FACTORY_H +#define SRV_CONFIG_MGR_FACTORY_H + +#include +#include +#include + +#include + +#include + +namespace isc { +namespace dhcp { + +/// @brief No configuration manager exception +/// +/// Thrown if an attempt is made to get a reference to the current configuration +/// manager and none is currently available. +class NoServerConfigManager : public Exception { +public: + NoServerConfigManager(const char* file, size_t line, const char* what) + : isc::Exception(file, line, what) { + } +}; + +/// @brief Server Configuration Manager Factory +/// +/// This class comprises nothing but static methods used to create a server +/// configuration manager. It analyzes the database information passed to the +/// creation function and instantiates an appropriate server configuration +/// manager +/// based on the type requested. +/// +/// Strictly speaking these functions could be stand-alone functions. However, +/// it is convenient to encapsulate them in a class for naming purposes. +/// +/// @todo: Will need to develop some form of registration mechanism for +/// user-supplied backends (so that there is no need to modify the code). +class SrvConfigMgrFactory { +public: + /// @brief Create an instance of a server configuration manager. + /// + /// Each database backend has its own server configuration manager type. + /// This static method sets the "current" server configuration manager + /// to be a manager of the appropriate type. The actual server + /// configuration + /// is returned by the "instance" method. + /// + /// @note When called, the current server configuration manager is + /// always destroyed and a new one created - even if + /// the parameters are the same. + /// + /// dbaccess is a generic way of passing parameters. Parameters are passed + /// in the "name=value" format, separated by spaces. The data MUST include + /// a keyword/value pair of the form "type=dbtype" giving the database + /// type, e.q. "mysql" or "sqlite3". + /// + /// @param dbaccess Database access parameters. These are in the form of + /// "keyword=value" pairs, separated by spaces. They are backend- + /// -end specific, although must include the "type" keyword which + /// gives the backend in use. + /// + /// @throw isc::InvalidParameter dbaccess string does not contain the "type" + /// keyword. + /// @throw isc::dhcp::InvalidType The "type" keyword in dbaccess does not + /// identify a supported backend. + static void create(const std::string& dbaccess); + + /// @brief Destroy server configuration manager + /// + /// Destroys the current server configuration manager object. This should + /// have the effect + /// of closing the database connection. The method is a no-op if no + /// server configuration manager is available. + static void destroy(); + + /// @brief Return current server configuration manager + /// + /// Returns an instance of the "current" server configuration manager. + /// An exception will be thrown if none is available. + /// + /// @throw isc::dhcp::NoLeaseManager No lease manager is available: use + /// create() to create one before calling this method. + static SrvConfigMgr& instance(); + + static bool initialized(); + +private: + /// @brief Hold pointer to server configuration manager + /// + /// Holds a pointer to the singleton server configuration manager. + /// The singleton is encapsulated in this method to avoid a "static + /// initialization + /// fiasco" if defined in an external static variable. + static boost::scoped_ptr& getConfigurationMgrPtr(); +}; + +} // namespace dhcp +} // namespace isc + +#endif // SRV_CONFIG_MGR_FACTORY_H diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index ae4aefe306..9af8c2bb22 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -40,10 +40,10 @@ prefixLessThanFirstAddress(const IOAddress& prefix, const PoolPtr& pool) { bool comparePoolFirstAddress(const PoolPtr& pool1, const PoolPtr& pool2) { return (pool1->getFirstAddress() < pool2->getFirstAddress()); -}; - } +} // namespace + namespace isc { namespace dhcp { @@ -749,5 +749,5 @@ Subnet6::toElement() const { } -} // end of isc::dhcp namespace -} // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 176e3dbb43..1e7da96348 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -787,7 +787,7 @@ typedef boost::multi_index_container< //@} -} // end of isc::dhcp namespace -} // end of isc namespace +} // namespace dhcp +} // namespace isc #endif // SUBNET_H diff --git a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc index 64b7b842bd..32b3a7e922 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc @@ -2439,6 +2439,6 @@ TEST_F(AllocEngine4Test, reservedAddressExistingLeaseStat) { EXPECT_FALSE(ctx.fake_allocation_); } -}; // namespace test -}; // namespace dhcp -}; // namespace isc +} // namespace test +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc index 0a8afebc32..4803480e68 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc @@ -112,7 +112,7 @@ TEST_F(AllocEngine6Test, pdFakeAlloc6) { // We should not have bumped the assigned counter EXPECT_TRUE(testStatistics("assigned-pds", 0, subnet_->getID())); -}; +} // This test checks if the allocation with a hint that is valid (in range, // in pool and free) can succeed @@ -2696,6 +2696,6 @@ TEST_F(SharedNetworkAlloc6Test, requestRunningOut) { EXPECT_TRUE(leases.empty()); } -}; // namespace test -}; // namespace dhcp -}; // namespace isc +} // namespace test +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc index 81eab8f28b..1b4dffd086 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc @@ -42,7 +42,7 @@ namespace test { bool testStatistics(const std::string& stat_name, const int64_t exp_value, const SubnetID subnet_id) { try { - std::string name = (!subnet_id ? stat_name : + std::string name = (!subnet_id ? stat_name : StatsMgr::generateName("subnet", subnet_id, stat_name)); ObservationPtr observation = StatsMgr::instance().getObservation(name); if (observation) { @@ -567,6 +567,6 @@ AllocEngine4Test::AllocEngine4Test() { ctx_.query_.reset(new Pkt4(DHCPREQUEST, 1234)); } -}; // namespace test -}; // namespace dhcp -}; // namespace isc +} // namespace test +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/tests/cql_connection_unittest.cc b/src/lib/dhcpsrv/tests/cql_connection_unittest.cc index 5d66802431..42787050d6 100644 --- a/src/lib/dhcpsrv/tests/cql_connection_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_connection_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Deutsche Telekom AG. +// Copyright (C) 2017-2018 Deutsche Telekom AG. // // 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 @@ -51,11 +51,11 @@ TEST(CqlConnection, statementMapHash) { map.insert({tag2, CqlTaggedStatement(tag2, "DELETE FROM world.evil")}); // Make sure the first one was overwritten. - char const* const tag1_text = map.find(tag1)->second.text_; - char const* const tag2_text = map.find(tag2)->second.text_; - EXPECT_TRUE(tag1_text); - EXPECT_TRUE(tag2_text); - ASSERT_EQ(std::strcmp(tag1_text, tag2_text), 0); + std::string const tag1_text = map.find(tag1)->second.text_; + std::string const tag2_text = map.find(tag2)->second.text_; + EXPECT_NE(tag1_text.size(), 0); + EXPECT_NE(tag2_text.size(), 0); + ASSERT_EQ(tag1_text, tag2_text); ASSERT_EQ(map.size(), 1u); } diff --git a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc index 46c21e5337..7164320222 100644 --- a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc @@ -1,5 +1,5 @@ // Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2016-2017 Deutsche Telekom AG. +// Copyright (C) 2016-2018 Deutsche Telekom AG. // // Author: Andrei Pavel // @@ -297,6 +297,18 @@ TEST_F(CqlHostDataSourceTest, basic4HWAddr) { testBasic4(Host::IDENT_HWADDR); } +// Verifies that IPv4 host reservation with options can have a max value +// for dhcp4_subnet id +TEST_F(CqlHostDataSourceTest, maxSubnetId4) { + testMaxSubnetId4(); +} + +// Verifies that IPv6 host reservation with options can have a max value +// for dhcp6_subnet id +TEST_F(CqlHostDataSourceTest, maxSubnetId6) { + testMaxSubnetId6(); +} + // Test verifies if a host reservation can be added and later retrieved by IPv4 // address. Host uses client-id (DUID) as identifier. TEST_F(CqlHostDataSourceTest, basic4ClientId) { diff --git a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc index 74beea413a..21603e73d0 100644 --- a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc @@ -1,5 +1,5 @@ // Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC") -// Copyright (C) 2015-2017 Deutsche Telekom AG. +// Copyright (C) 2015-2018 Deutsche Telekom AG. // // Authors: Razvan Becheriu // Andrei Pavel @@ -221,7 +221,7 @@ class CqlLeaseMgrTest : public GenericLeaseMgrTest { } // This is the CQL implementation for - // GenericLeaseMgrTest::testGetExpiredLeases4(). + // GenericLeaseMgrTest::testGetExpiredLeases6(). // The GenericLeaseMgrTest implementation checks for the order of expired // leases to be from the most expired to the least expired. Cassandra // doesn't support ORDER BY without imposing a EQ / IN restriction on the @@ -283,8 +283,7 @@ class CqlLeaseMgrTest : public GenericLeaseMgrTest { } // Retrieve expired leases again. The limit of 0 means return all - // expired - // leases. + // expired leases. ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0)); // The same leases should be returned. @@ -485,7 +484,7 @@ TEST_F(CqlLeaseMgrTest, getName) { /// @brief Check that getVersion() returns the expected version TEST_F(CqlLeaseMgrTest, checkVersion) { // Check version - pair version; + VersionPair version; ASSERT_NO_THROW(version = lmptr_->getVersion()); EXPECT_EQ(CQL_SCHEMA_VERSION_MAJOR, version.first); EXPECT_EQ(CQL_SCHEMA_VERSION_MINOR, version.second); @@ -717,12 +716,12 @@ TEST_F(CqlLeaseMgrTest, nullDuid) { testNullDuid(); } -/// @brief Tests whether memfile can store and retrieve hardware addresses +/// @brief Tests whether CQL can store and retrieve hardware addresses TEST_F(CqlLeaseMgrTest, testLease6Mac) { testLease6MAC(); } -/// @brief Tests whether memfile can store and retrieve hardware addresses +/// @brief Tests whether CQL can store and retrieve hardware addresses TEST_F(CqlLeaseMgrTest, testLease6HWTypeAndSource) { testLease6HWTypeAndSource(); } @@ -753,14 +752,14 @@ TEST_F(CqlLeaseMgrTest, recountLeaseStats6) { testRecountLeaseStats6(); } -// @brief Tests that leases from specific subnet can be removed. +/// @brief Tests that leases from specific subnet can be removed. /// @todo: uncomment this once lease wipe is implemented /// for Cassandra (see #5485) TEST_F(CqlLeaseMgrTest, DISABLED_wipeLeases4) { testWipeLeases4(); } -// @brief Tests that leases from specific subnet can be removed. +/// @brief Tests that leases from specific subnet can be removed. /// @todo: uncomment this once lease wipe is implemented /// for Cassandra (see #5485) TEST_F(CqlLeaseMgrTest, DISABLED_wipeLeases6) { diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index 90a7f28e7a..71984724f7 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -1611,7 +1611,7 @@ GenericLeaseMgrTest::testUpdateLease6() { void GenericLeaseMgrTest::testRecreateLease4() { // Create a lease. - std::vector leases = createLeases4(); + Lease4Collection leases = createLeases4(); // Copy the lease so as we can freely modify it. Lease4Ptr lease(new Lease4(*leases[0])); @@ -1651,7 +1651,7 @@ GenericLeaseMgrTest::testRecreateLease4() { void GenericLeaseMgrTest::testRecreateLease6() { // Create a lease. - std::vector leases = createLeases6(); + Lease6Collection leases = createLeases6(); // Copy the lease so as we can freely modify it. Lease6Ptr lease(new Lease6(*leases[0])); @@ -1784,7 +1784,7 @@ GenericLeaseMgrTest::testGetExpiredLeases4() { } // Remember expired leases returned. - std::vector saved_expired_leases = expired_leases; + Lease4Collection saved_expired_leases = expired_leases; // Remove expired leases again. expired_leases.clear(); @@ -1905,7 +1905,7 @@ GenericLeaseMgrTest::testGetExpiredLeases6() { } // Remember expired leases returned. - std::vector saved_expired_leases = expired_leases; + Lease6Collection saved_expired_leases = expired_leases; // Remove expired leases again. expired_leases.clear(); @@ -2844,7 +2844,7 @@ LeaseMgrDbLostCallbackTest::testDbLostCallback() { // Verify we can execute a query. LeaseMgr& lm = LeaseMgrFactory::instance(); - pair version; + VersionPair version; ASSERT_NO_THROW(version = lm.getVersion()); // Now close the sql socket out from under backend client @@ -3148,6 +3148,6 @@ GenericLeaseMgrTest::testLeaseStatsQuery6() { } } -}; // namespace test -}; // namespace dhcp -}; // namespace isc +} // namespace test +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index 09b0be7520..90bbf8f55b 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -67,7 +67,8 @@ class GenericLeaseMgrTest : public ::testing::Test { /// /// @param address Address to use for the initialization /// - /// @return Lease6Ptr. This will not point to anything if the initialization + /// @return Lease6Ptr. This will not point to anything if the + /// initialization /// failed (e.g. unknown address). Lease6Ptr initializeLease6(std::string address); @@ -211,8 +212,9 @@ class GenericLeaseMgrTest : public ::testing::Test { /// @brief Basic Lease4 Checks /// - /// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id), - /// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id. + /// Checks that the addLease, getLease4(by address), + /// getLease4(hwaddr,subnet_id), updateLease4() and + /// deleteLease can handle NULL client-id. /// (client-id is optional and may not be present) /// /// @todo: check if it does overlap with @ref testGetLease4NullClientId() @@ -227,14 +229,15 @@ class GenericLeaseMgrTest : public ::testing::Test { /// @brief Basic Lease6 Checks /// - /// Checks that the addLease, getLease6 (by address) and deleteLease (with an - /// IPv6 address) works. + /// Checks that the addLease, getLease6 (by address) and deleteLease (with + /// an IPv6 address) works. void testBasicLease6(); /// @brief Checks that invalid dates are safely handled. void testMaxDate6(); - /// @brief Checks that Lease6 can be stored with and without a hardware address. + /// @brief Checks that Lease6 can be stored with and without a hardware + /// address. void testLease6MAC(); /// @brief Checks that Lease6 stores hardware type and hardware source. @@ -333,7 +336,8 @@ class GenericLeaseMgrTest : public ::testing::Test { /// - reclaimed leases are not returned. void testGetExpiredLeases6(); - /// @brief Checks that declined IPv4 leases that have expired can be retrieved. + /// @brief Checks that declined IPv4 leases that have expired can be + /// retrieved. /// /// This test checks that the following: /// - all expired and not reclaimed leases are returned, regardless if @@ -343,7 +347,8 @@ class GenericLeaseMgrTest : public ::testing::Test { /// expired void testGetDeclinedLeases4(); - /// @brief Checks that declined IPv6 leases that have expired can be retrieved. + /// @brief Checks that declined IPv6 leases that have expired can be + /// retrieved. /// /// This test checks that the following: /// - all expired and not reclaimed leases are returned, regardless if @@ -354,7 +359,7 @@ class GenericLeaseMgrTest : public ::testing::Test { void testGetDeclinedLeases6(); /// @brief Checks that selected expired-reclaimed IPv6 leases - /// are removed. + /// are removed. /// /// This creates a number of DHCPv6 leases and marks some of them /// as expired-reclaimed. It later verifies that the expired-reclaimed @@ -362,7 +367,7 @@ class GenericLeaseMgrTest : public ::testing::Test { void testDeleteExpiredReclaimedLeases6(); /// @brief Checks that selected expired-reclaimed IPv4 leases - /// are removed. + /// are removed. /// /// This creates a number of DHCPv4 leases and marks some of them /// as expired-reclaimed. It later verifies that the expired-reclaimed @@ -427,13 +432,13 @@ class GenericLeaseMgrTest : public ::testing::Test { void checkQueryAgainstRowSet(const LeaseStatsQueryPtr& qry, const RowSet& row_set); /// @brief String forms of IPv4 addresses - std::vector straddress4_; + std::vector straddress4_; /// @brief IOAddress forms of IPv4 addresses std::vector ioaddress4_; /// @brief String forms of IPv6 addresses - std::vector straddress6_; + std::vector straddress6_; /// @brief Types of IPv6 Leases std::vector leasetype6_; @@ -483,7 +488,7 @@ class LeaseMgrDbLostCallbackTest : public ::testing::Test { /// @brief Verifies open failures do NOT invoke db lost callback /// - /// The db lost callback should only be invoked after succesfully + /// The db lost callback should only be invoked after successfully /// opening the DB and then subsequently losing it. Failing to /// open should be handled directly by the application layer. void testNoCallbackOnOpenFailure(); @@ -510,8 +515,8 @@ class LeaseMgrDbLostCallbackTest : public ::testing::Test { }; -}; // namespace test -}; // namespace dhcp -}; // namespace isc +} // namespace test +} // namespace dhcp +} // namespace isc #endif diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index 28e5c97e9b..593da31ed1 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -296,10 +296,16 @@ class ConcreteLeaseMgr : public LeaseMgr { } /// @brief Returns backend version. - virtual std::pair getVersion() const { + virtual VersionPair getVersion() const { return (make_pair(uint32_t(0), uint32_t(0))); } + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction() {return true;}; + /// @brief Commit transactions virtual void commit() { } @@ -413,4 +419,4 @@ TEST (LeaseStatsQueryTest, subnetRangeCtor) { // are purely virtual, so we would only call ConcreteLeaseMgr methods. // Those methods are just stubs that do not return anything. -}; // end of anonymous namespace +} // namespace diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index dcacbc403e..6c6c2f419e 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -253,7 +253,7 @@ TEST_F(MySqlLeaseMgrTest, getName) { /// @brief Check that getVersion() returns the expected version TEST_F(MySqlLeaseMgrTest, checkVersion) { // Check version - pair version; + VersionPair version; ASSERT_NO_THROW(version = lmptr_->getVersion()); EXPECT_EQ(MYSQL_SCHEMA_VERSION_MAJOR, version.first); EXPECT_EQ(MYSQL_SCHEMA_VERSION_MINOR, version.second); @@ -495,12 +495,12 @@ TEST_F(MySqlLeaseMgrTest, nullDuid) { testNullDuid(); } -/// @brief Tests whether memfile can store and retrieve hardware addresses +/// @brief Tests whether MySQL can store and retrieve hardware addresses TEST_F(MySqlLeaseMgrTest, testLease6Mac) { testLease6MAC(); } -/// @brief Tests whether memfile can store and retrieve hardware addresses +/// @brief Tests whether MySQL can store and retrieve hardware addresses TEST_F(MySqlLeaseMgrTest, testLease6HWTypeAndSource) { testLease6HWTypeAndSource(); } @@ -531,12 +531,12 @@ TEST_F(MySqlLeaseMgrTest, recountLeaseStats6) { testRecountLeaseStats6(); } -// @brief Tests that leases from specific subnet can be removed. +/// @brief Tests that leases from specific subnet can be removed. TEST_F(MySqlLeaseMgrTest, DISABLED_wipeLeases4) { testWipeLeases4(); } -// @brief Tests that leases from specific subnet can be removed. +/// @brief Tests that leases from specific subnet can be removed. TEST_F(MySqlLeaseMgrTest, DISABLED_wipeLeases6) { testWipeLeases6(); } diff --git a/src/lib/dhcpsrv/tests/pgsql_exchange_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_exchange_unittest.cc index 6f6ba77dc5..34a2b72ace 100644 --- a/src/lib/dhcpsrv/tests/pgsql_exchange_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_exchange_unittest.cc @@ -598,7 +598,7 @@ TEST_F(PgSqlBasicsTest, smallIntTest) { ints.push_back(-1); ints.push_back(0); ints.push_back(0x7fff); - ints.push_back(0xffff); + ints.push_back(static_cast(0xffff)); // Insert a row for each reference value PsqlBindArrayPtr bind_array; @@ -920,4 +920,4 @@ TEST_F(PgSqlBasicsTest, timeStampTest) { MAX_DB_TIME), BadValue); } -}; // namespace +} // namespace diff --git a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc index df2122d475..6864804d64 100644 --- a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc @@ -236,7 +236,7 @@ bool db_lost_callback(ReconnectCtlPtr /* db_conn_retry */) { } /// @brief Make sure open failures do NOT invoke db lost callback -/// The db lost callback should only be invoked after succesfully +/// The db lost callback should only be invoked after successfully /// opening the DB and then subsequently losing it. Failing to /// open should be handled directly by the application layer. /// There is simply no good way to break the connection in a diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index f0b3ec7b47..c7c261d1f6 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -242,7 +242,7 @@ TEST_F(PgSqlLeaseMgrTest, getName) { /// @brief Check that getVersion() returns the expected version TEST_F(PgSqlLeaseMgrTest, checkVersion) { // Check version - pair version; + VersionPair version; ASSERT_NO_THROW(version = lmptr_->getVersion()); EXPECT_EQ(PG_SCHEMA_VERSION_MAJOR, version.first); EXPECT_EQ(PG_SCHEMA_VERSION_MINOR, version.second); @@ -484,12 +484,12 @@ TEST_F(PgSqlLeaseMgrTest, nullDuid) { testNullDuid(); } -/// @brief Tests whether Postgres can store and retrieve hardware addresses +/// @brief Tests whether PostgreSQL can store and retrieve hardware addresses TEST_F(PgSqlLeaseMgrTest, testLease6Mac) { testLease6MAC(); } -/// @brief Tests whether Postgres can store and retrieve hardware addresses +/// @brief Tests whether PostgreSQL can store and retrieve hardware addresses TEST_F(PgSqlLeaseMgrTest, testLease6HWTypeAndSource) { testLease6HWTypeAndSource(); } @@ -520,12 +520,12 @@ TEST_F(PgSqlLeaseMgrTest, recountLeaseStats6) { testRecountLeaseStats6(); } -// @brief Tests that leases from specific subnet can be removed. +/// @brief Tests that leases from specific subnet can be removed. TEST_F(PgSqlLeaseMgrTest, DISABLED_wipeLeases4) { testWipeLeases4(); } -// @brief Tests that leases from specific subnet can be removed. +/// @brief Tests that leases from specific subnet can be removed. TEST_F(PgSqlLeaseMgrTest, DISABLED_wipeLeases6) { testWipeLeases6(); } diff --git a/src/lib/dhcpsrv/testutils/cql_schema.cc b/src/lib/dhcpsrv/testutils/cql_schema.cc index abd7c19859..cad4d968b1 100644 --- a/src/lib/dhcpsrv/testutils/cql_schema.cc +++ b/src/lib/dhcpsrv/testutils/cql_schema.cc @@ -55,6 +55,7 @@ void createCqlSchema(bool force_wipe, bool show_err) { if (force_wipe || !softWipeEnabled()) { runCqlScript(DATABASE_SCRIPTS_DIR, "cql/dhcpdb_create.cql", show_err); + runCqlScript(DATABASE_SCRIPTS_DIR, "cql/configdb_create.cql", show_err); } } diff --git a/src/lib/dhcpsrv/testutils/cql_schema.h b/src/lib/dhcpsrv/testutils/cql_schema.h index 91377f96db..aff1f82b99 100644 --- a/src/lib/dhcpsrv/testutils/cql_schema.h +++ b/src/lib/dhcpsrv/testutils/cql_schema.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2017 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 @@ -20,7 +20,8 @@ extern const char* CQL_VALID_TYPE; /// Return valid connection string /// /// @return valid CQL connection string. -std::string validCqlConnectionString(); +std::string +validCqlConnectionString(); /// @brief Clear everything from the database /// @@ -35,7 +36,8 @@ std::string validCqlConnectionString(); /// @param force_wipe forces wipe of the database, even if /// KEA_TEST_CASSANDRA_WIPE is set. /// @param show_err flag which governs whether or not stderr is suppressed. -void destroyCqlSchema(bool force_wipe, bool show_err = false); +void +destroyCqlSchema(bool force_wipe, bool show_err = false); /// @brief Create the CQL Schema /// @@ -43,6 +45,10 @@ void destroyCqlSchema(bool force_wipe, bool show_err = false); /// /// /cql/dhcpdb_create.cql /// +/// and the configuration upgrade script: +/// +/// /cql/configdb_create.cql +/// /// to the unit test CQL database. If the script fails, the invoking test /// will fail. The output of stderr is suppressed unless the parameter, /// show_err is true. @@ -50,7 +56,8 @@ void destroyCqlSchema(bool force_wipe, bool show_err = false); /// @param force_wipe forces wipe of the database, even if /// KEA_TEST_CASSANDRA_WIPE is set. /// @param show_err flag which governs whether or not stderr is suppressed. -void createCqlSchema(bool force_wipe, bool show_err = false); +void +createCqlSchema(bool force_wipe, bool show_err = false); /// @brief Run a CQL script against the CQL unit test database /// @@ -62,8 +69,10 @@ void createCqlSchema(bool force_wipe, bool show_err = false); /// @param path - path (if not blank) of the script to execute /// @param script_name - file name of the path to execute /// @param show_err flag which governs whether or not stderr is suppressed. -void runCqlScript(const std::string& path, const std::string& script_name, - bool show_err); +void +runCqlScript(const std::string& path, + const std::string& script_name, + bool show_err); /// @brief Returns status if the soft-wipe is enabled or not. /// @@ -87,9 +96,11 @@ void runCqlScript(const std::string& path, const std::string& script_name, /// $ tests/libdhcpsrv_unittests --gtest_filter=CqlLeaseMgrTest.* /// /// @return true if soft-wipe is enabled, false otherwise -bool softWipeEnabled(); -}; -}; -}; +bool +softWipeEnabled(); + +} // namespace test +} // namespace dhcp +} // namespace isc -#endif +#endif // TEST_CQL_SCHEMA_H diff --git a/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc index 36a37efb48..41be4192a0 100644 --- a/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc +++ b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.cc @@ -31,6 +31,12 @@ Dhcp4o6TestIpc::open() { } } +void +Dhcp4o6TestIpc::close() { + // Use the base IPC to close the socket + Dhcp4o6IpcBase::close(); +} + void Dhcp4o6TestIpc::receiveHandler() { pkt_received_ = receive(); diff --git a/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h index 5109de06c3..6af2295395 100644 --- a/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h +++ b/src/lib/dhcpsrv/testutils/dhcp4o6_test_ipc.h @@ -43,6 +43,9 @@ class Dhcp4o6TestIpc : public Dhcp4o6IpcBase { /// over the socket. virtual void open(); + /// @brief Close the IPC socket. + virtual void close(); + /// @brief Retrieve port which socket is bound to. uint16_t getPort() const { return (port_); diff --git a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc index 9a58475edf..18885cc862 100644 --- a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc @@ -290,7 +290,8 @@ GenericHostDataSourceTest::testMaxSubnetId4() { EXPECT_FALSE(host_by_id); } -void GenericHostDataSourceTest::testMaxSubnetId6() { +void +GenericHostDataSourceTest::testMaxSubnetId6() { std::vector ident; ident = HostDataSourceUtils::generateIdentifier(); @@ -971,7 +972,8 @@ GenericHostDataSourceTest::testMultipleReservationsDifferentOrder() { host2->getIPv6Reservations()); } -void GenericHostDataSourceTest::testOptionsReservations4(const bool formatted, +void +GenericHostDataSourceTest::testOptionsReservations4(const bool formatted, ConstElementPtr user_context) { HostPtr host = HostDataSourceUtils::initializeHost4("192.0.2.5", Host::IDENT_HWADDR); // Add a bunch of DHCPv4 and DHCPv6 options for the host. @@ -999,7 +1001,8 @@ void GenericHostDataSourceTest::testOptionsReservations4(const bool formatted, ASSERT_NO_FATAL_FAILURE(HostDataSourceUtils::compareHosts(host, host_by_addr)); } -void GenericHostDataSourceTest::testOptionsReservations6(const bool formatted, +void +GenericHostDataSourceTest::testOptionsReservations6(const bool formatted, ConstElementPtr user_context) { HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_DUID, false); // Add a bunch of DHCPv4 and DHCPv6 options for the host. @@ -1335,7 +1338,8 @@ GenericHostDataSourceTest::stressTest(unsigned int nOfHosts /* = 0xfffdU */) { << std::endl; } -void GenericHostDataSourceTest::testDeleteByAddr4() { +void +GenericHostDataSourceTest::testDeleteByAddr4() { // Make sure we have a pointer to the host data source. ASSERT_TRUE(hdsptr_); @@ -1362,7 +1366,8 @@ void GenericHostDataSourceTest::testDeleteByAddr4() { EXPECT_FALSE(after); } -void GenericHostDataSourceTest::testDeleteById4() { +void +GenericHostDataSourceTest::testDeleteById4() { // Make sure we have a pointer to the host data source. ASSERT_TRUE(hdsptr_); @@ -1399,7 +1404,8 @@ void GenericHostDataSourceTest::testDeleteById4() { // Test checks when a IPv4 host with options is deleted that the options are // deleted as well. -void GenericHostDataSourceTest::testDeleteById4Options() { +void +GenericHostDataSourceTest::testDeleteById4Options() { // Make sure we have a pointer to the host data source. ASSERT_TRUE(hdsptr_); @@ -1444,7 +1450,8 @@ void GenericHostDataSourceTest::testDeleteById4Options() { EXPECT_EQ(0, countDBOptions4()); } -void GenericHostDataSourceTest::testDeleteById6() { +void +GenericHostDataSourceTest::testDeleteById6() { // Make sure we have a pointer to the host data source. ASSERT_TRUE(hdsptr_); @@ -1479,7 +1486,8 @@ void GenericHostDataSourceTest::testDeleteById6() { EXPECT_FALSE(after); } -void GenericHostDataSourceTest::testDeleteById6Options() { +void +GenericHostDataSourceTest::testDeleteById6Options() { // Make sure we have a pointer to the host data source. ASSERT_TRUE(hdsptr_); diff --git a/src/lib/dhcpsrv/testutils/host_data_source_utils.cc b/src/lib/dhcpsrv/testutils/host_data_source_utils.cc index ad1f4f0e38..09239970e0 100644 --- a/src/lib/dhcpsrv/testutils/host_data_source_utils.cc +++ b/src/lib/dhcpsrv/testutils/host_data_source_utils.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcpsrv/testutils/memory_host_data_source.h b/src/lib/dhcpsrv/testutils/memory_host_data_source.h index 906a3630b2..40e969f76c 100644 --- a/src/lib/dhcpsrv/testutils/memory_host_data_source.h +++ b/src/lib/dhcpsrv/testutils/memory_host_data_source.h @@ -218,7 +218,7 @@ class MemHostDataSource : public virtual BaseHostDataSource { /// @brief Returns version this backend /// @return two numbers that each represent practical value of this backend. - virtual std::pair getVersion() const { + virtual VersionPair getVersion() const { return (std::make_pair(0,0)); } diff --git a/src/lib/dhcpsrv/testutils/mysql_schema.cc b/src/lib/dhcpsrv/testutils/mysql_schema.cc index 6b1225bfb7..f5a9c68adc 100644 --- a/src/lib/dhcpsrv/testutils/mysql_schema.cc +++ b/src/lib/dhcpsrv/testutils/mysql_schema.cc @@ -35,6 +35,7 @@ void destroyMySQLSchema(bool show_err) { void createMySQLSchema(bool show_err) { runMySQLScript(DATABASE_SCRIPTS_DIR, "mysql/dhcpdb_create.mysql", show_err); + runMySQLScript(DATABASE_SCRIPTS_DIR, "mysql/configdb_create.mysql", show_err); } void runMySQLScript(const std::string& path, const std::string& script_name, diff --git a/src/lib/dhcpsrv/testutils/mysql_schema.h b/src/lib/dhcpsrv/testutils/mysql_schema.h index d0665eb46a..460f685b35 100644 --- a/src/lib/dhcpsrv/testutils/mysql_schema.h +++ b/src/lib/dhcpsrv/testutils/mysql_schema.h @@ -41,6 +41,10 @@ void destroyMySQLSchema(bool show_err = false); /// /// /mysql/dhcpdb_create.mysql /// +/// and the configuration update script: +/// +/// /mysql/configdb_create.mysql +/// /// to the unit test MySQL database. If the script fails, the invoking test /// will fail. The output of stderr is suppressed unless the parameter, /// show_err is true. @@ -61,8 +65,8 @@ void createMySQLSchema(bool show_err = false); void runMySQLScript(const std::string& path, const std::string& script_name, bool show_err); -}; -}; -}; +} // namespace test +} // namespace dhcp +} // namespace isc #endif diff --git a/src/lib/dhcpsrv/testutils/pgsql_schema.cc b/src/lib/dhcpsrv/testutils/pgsql_schema.cc index fc7d1c3d35..3c4bc84268 100644 --- a/src/lib/dhcpsrv/testutils/pgsql_schema.cc +++ b/src/lib/dhcpsrv/testutils/pgsql_schema.cc @@ -35,6 +35,7 @@ void destroyPgSQLSchema(bool show_err) { void createPgSQLSchema(bool show_err) { runPgSQLScript(DATABASE_SCRIPTS_DIR, "pgsql/dhcpdb_create.pgsql", show_err); + runPgSQLScript(DATABASE_SCRIPTS_DIR, "pgsql/configdb_create.pgsql", show_err); } void runPgSQLScript(const std::string& path, const std::string& script_name, diff --git a/src/lib/dhcpsrv/testutils/pgsql_schema.h b/src/lib/dhcpsrv/testutils/pgsql_schema.h index 7b4cf54ecf..9240875aad 100644 --- a/src/lib/dhcpsrv/testutils/pgsql_schema.h +++ b/src/lib/dhcpsrv/testutils/pgsql_schema.h @@ -41,6 +41,10 @@ void destroyPgSQLSchema(bool show_err = false); /// /// /pgsql/dhcpdb_create.pgsql /// +/// and the configuration upgrade script: +/// +/// /pgsql/configdb_create.pgsql +/// /// to the unit test Postgresql database. If the script fails, the invoking /// test will fail. The output of stderr is suppressed unless the parameter, /// show_err is true. @@ -61,8 +65,8 @@ void createPgSQLSchema(bool show_err = false); void runPgSQLScript(const std::string& path, const std::string& script_name, bool show_err); -}; -}; -}; +} // namespace test +} // namespace dhcp +} // namespace isc #endif diff --git a/src/lib/eval/Makefile.am b/src/lib/eval/Makefile.am index 4308d43c96..5e28cb7ea9 100644 --- a/src/lib/eval/Makefile.am +++ b/src/lib/eval/Makefile.am @@ -92,6 +92,7 @@ parser: lexer.cc location.hh position.hh stack.hh parser.cc parser.h # will cause parser to print out its internal state. location.hh position.hh stack.hh parser.cc parser.h: parser.yy $(YACC) --defines=parser.h -o parser.cc parser.yy + $(YACC) --graph --defines=parser.h -o parser.cc parser.yy lexer.cc: lexer.ll $(LEX) --prefix eval -o lexer.cc lexer.ll diff --git a/src/lib/eval/eval_log.cc b/src/lib/eval/eval_log.cc index 6492ad69c7..ae9f7fb934 100644 --- a/src/lib/eval/eval_log.cc +++ b/src/lib/eval/eval_log.cc @@ -14,7 +14,9 @@ namespace isc { namespace dhcp { isc::log::Logger eval_logger("eval"); +const int EVAL_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +const int EVAL_DBG_STACK = isc::log::DBGLVL_TRACE_DETAIL_DATA; -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/eval/eval_log.h b/src/lib/eval/eval_log.h index 0b33c43717..074960ba76 100644 --- a/src/lib/eval/eval_log.h +++ b/src/lib/eval/eval_log.h @@ -19,11 +19,11 @@ namespace dhcp { /// Note that higher numbers equate to more verbose (and detailed) output. // The first level traces normal operations, -const int EVAL_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC; +extern const int EVAL_DBG_TRACE; // Additional information on the calls. Report the values that were // popped from or pushed to the value stack. -const int EVAL_DBG_STACK = isc::log::DBGLVL_TRACE_DETAIL_DATA; +extern const int EVAL_DBG_STACK; /// @brief Eval Logger /// @@ -32,7 +32,7 @@ const int EVAL_DBG_STACK = isc::log::DBGLVL_TRACE_DETAIL_DATA; /// space. extern isc::log::Logger eval_logger; -} // namespace dhcp -} // namespace isc +} // namespace dhcp +} // namespace isc #endif // EVAL_LOG_H diff --git a/src/lib/eval/lexer.cc b/src/lib/eval/lexer.cc index d5e871c95a..8549377461 100644 --- a/src/lib/eval/lexer.cc +++ b/src/lib/eval/lexer.cc @@ -1045,7 +1045,7 @@ static isc::eval::location loc; namespace { bool start_token_flag = false; isc::eval::EvalContext::ParserType start_token_value; -}; +} // namespace /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::eval::EvalContext::fatal(msg) @@ -2933,6 +2933,6 @@ class Dummy { /* cppcheck-suppress unusedPrivateFunction */ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); } }; -} +} // namespace #endif /* !__clang_analyzer__ */ diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll index d990b013eb..e6b0bb7439 100644 --- a/src/lib/eval/lexer.ll +++ b/src/lib/eval/lexer.ll @@ -34,7 +34,7 @@ static isc::eval::location loc; namespace { bool start_token_flag = false; isc::eval::EvalContext::ParserType start_token_value; -}; +} // namespace /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::eval::EvalContext::fatal(msg) @@ -246,5 +246,5 @@ class Dummy { /* cppcheck-suppress unusedPrivateFunction */ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); } }; -} +} // namespace #endif /* !__clang_analyzer__ */ diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index 330bc60dfb..2faa3b2723 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -1019,7 +1019,7 @@ class TokenVendorClass : public TokenVendor { uint16_t index_; }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // end of isc::dhcp namespace +} // end of isc namespace #endif diff --git a/src/lib/exceptions/exceptions.h b/src/lib/exceptions/exceptions.h index b6396908cf..08e217a1e0 100644 --- a/src/lib/exceptions/exceptions.h +++ b/src/lib/exceptions/exceptions.h @@ -13,6 +13,28 @@ namespace isc { +class TransactionException : public std::exception { +public: + TransactionException(const char* file, size_t line, const char* what) : + file_(file), line_(line), what_(what) {} + virtual ~TransactionException() throw() {} +private: + void operator=(const TransactionException& src); +public: + virtual const char* what() const throw() { + const char* whatstr = "Transaction Exception"; + try { + whatstr = what_.c_str(); + } catch (...) { + } + return (whatstr); + } +private: + const char* const file_; + size_t line_; + const std::string what_; +}; + /// /// This is a base class for exceptions thrown from the DNS library module. /// Normally, the exceptions are thrown via a convenient shortcut macro, diff --git a/src/lib/hooks/hooks_config.h b/src/lib/hooks/hooks_config.h index 837727e40e..e5f4ac3f45 100644 --- a/src/lib/hooks/hooks_config.h +++ b/src/lib/hooks/hooks_config.h @@ -102,7 +102,7 @@ class HooksConfig : public isc::data::CfgToElement { isc::hooks::HookLibsCollection libraries_; }; -}; -}; +} +} #endif // HOOKS_CONFIG_H diff --git a/src/lib/hooks/libinfo.h b/src/lib/hooks/libinfo.h index 42b0441586..b300965783 100644 --- a/src/lib/hooks/libinfo.h +++ b/src/lib/hooks/libinfo.h @@ -36,7 +36,7 @@ typedef boost::shared_ptr HookLibsCollectionPtr; /// @brief Extracts library names from full library information structure std::vector extractNames(const HookLibsCollection& libinfo); -}; -}; +} +} #endif diff --git a/src/lib/hooks/library_handle.h b/src/lib/hooks/library_handle.h index 983c8802a0..6618eccfaf 100644 --- a/src/lib/hooks/library_handle.h +++ b/src/lib/hooks/library_handle.h @@ -20,7 +20,7 @@ class CalloutManager; /// Typedef for a callout pointer. (Callouts must have "C" linkage.) extern "C" { typedef int (*CalloutPtr)(CalloutHandle&); -}; +} /// @brief Library handle /// diff --git a/src/lib/hooks/tests/parking_lots_unittest.cc b/src/lib/hooks/tests/parking_lots_unittest.cc index 99b62ca83f..93dd92e84b 100644 --- a/src/lib/hooks/tests/parking_lots_unittest.cc +++ b/src/lib/hooks/tests/parking_lots_unittest.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/client.cc b/src/lib/http/client.cc index 5b0da858e2..58ba929ed3 100644 --- a/src/lib/http/client.cc +++ b/src/lib/http/client.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/connection.cc b/src/lib/http/connection.cc index c6f3f36caa..2075dc6fe3 100644 --- a/src/lib/http/connection.cc +++ b/src/lib/http/connection.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/connection_pool.cc b/src/lib/http/connection_pool.cc index 8d39c02d4d..770e193b55 100644 --- a/src/lib/http/connection_pool.cc +++ b/src/lib/http/connection_pool.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/lib/http/date_time.cc b/src/lib/http/date_time.cc index a18116704f..4a24d86866 100644 --- a/src/lib/http/date_time.cc +++ b/src/lib/http/date_time.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/http_header.cc b/src/lib/http/http_header.cc index 3e62cc14a8..b5d6c79bb1 100644 --- a/src/lib/http/http_header.cc +++ b/src/lib/http/http_header.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/http_log.cc b/src/lib/http/http_log.cc index 64892f26f8..72559121b3 100644 --- a/src/lib/http/http_log.cc +++ b/src/lib/http/http_log.cc @@ -6,6 +6,8 @@ /// Defines the logger used by the top-level component of kea-dhcp-ddns. +#include + #include namespace isc { diff --git a/src/lib/http/http_message.cc b/src/lib/http/http_message.cc index 0f7166dfed..ce012cafae 100644 --- a/src/lib/http/http_message.cc +++ b/src/lib/http/http_message.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include namespace isc { diff --git a/src/lib/http/http_message_parser_base.cc b/src/lib/http/http_message_parser_base.cc index a5d84c660e..078cec6ea8 100644 --- a/src/lib/http/http_message_parser_base.cc +++ b/src/lib/http/http_message_parser_base.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/lib/http/listener.cc b/src/lib/http/listener.cc index 643376942f..f123ddb88f 100644 --- a/src/lib/http/listener.cc +++ b/src/lib/http/listener.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/post_request.cc b/src/lib/http/post_request.cc index 62e4a34a73..e40c67f7c2 100644 --- a/src/lib/http/post_request.cc +++ b/src/lib/http/post_request.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include namespace isc { diff --git a/src/lib/http/post_request_json.cc b/src/lib/http/post_request_json.cc index 0c7d74f7dc..163a6e7f0c 100644 --- a/src/lib/http/post_request_json.cc +++ b/src/lib/http/post_request_json.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include using namespace isc::data; diff --git a/src/lib/http/request.cc b/src/lib/http/request.cc index 678f8b605d..0d6529f58f 100644 --- a/src/lib/http/request.cc +++ b/src/lib/http/request.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/request_parser.cc b/src/lib/http/request_parser.cc index fe9716646e..5b1f5fb27c 100644 --- a/src/lib/http/request_parser.cc +++ b/src/lib/http/request_parser.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/response.cc b/src/lib/http/response.cc index 13fd4a5b2c..b82e0dcfe5 100644 --- a/src/lib/http/response.cc +++ b/src/lib/http/response.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/response_creator.cc b/src/lib/http/response_creator.cc index 8c63fea41b..da74987ea7 100644 --- a/src/lib/http/response_creator.cc +++ b/src/lib/http/response_creator.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include namespace isc { diff --git a/src/lib/http/response_json.cc b/src/lib/http/response_json.cc index aa29d49b1f..3a8445d351 100644 --- a/src/lib/http/response_json.cc +++ b/src/lib/http/response_json.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/lib/http/response_parser.cc b/src/lib/http/response_parser.cc index 127d3a0418..b412a5b02b 100644 --- a/src/lib/http/response_parser.cc +++ b/src/lib/http/response_parser.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/lib/http/tests/run_unittests.cc b/src/lib/http/tests/run_unittests.cc index 581febb976..ba4f4ff0ed 100644 --- a/src/lib/http/tests/run_unittests.cc +++ b/src/lib/http/tests/run_unittests.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/http/url.cc b/src/lib/http/url.cc index fe68f58e6a..d7b161d28c 100644 --- a/src/lib/http/url.cc +++ b/src/lib/http/url.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/share/database/scripts/cql/.gitignore b/src/share/database/scripts/cql/.gitignore index d184e0fe31..5f4a721cae 100644 --- a/src/share/database/scripts/cql/.gitignore +++ b/src/share/database/scripts/cql/.gitignore @@ -1,2 +1,3 @@ +config_upgrade_0.0_to_1.0.sh +master_upgrade_0.0_to_1.0.sh upgrade_1.0_to_2.0.sh - diff --git a/src/share/database/scripts/cql/Makefile.am b/src/share/database/scripts/cql/Makefile.am index 46f8e26f8e..1947bf075f 100644 --- a/src/share/database/scripts/cql/Makefile.am +++ b/src/share/database/scripts/cql/Makefile.am @@ -1,8 +1,14 @@ SUBDIRS = . sqlscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts/cql -sqlscripts_DATA = dhcpdb_create.cql +sqlscripts_DATA = dhcpdb_create.cql sqlscripts_DATA += dhcpdb_drop.cql +sqlscripts_DATA += configdb_create.cql +sqlscripts_DATA += config_upgrade_0.0_to_1.0.cql +sqlscripts_DATA += config_upgrade_0.0_to_1.0.sh +sqlscripts_DATA += masterdb_create.cql +sqlscripts_DATA += master_upgrade_0.0_to_1.0.cql +sqlscripts_DATA += master_upgrade_0.0_to_1.0.sh sqlscripts_DATA += upgrade_1.0_to_2.0.sh sqlscripts_DATA += soft_wipe.cql diff --git a/src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.cql b/src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.cql new file mode 100644 index 0000000000..1e3e2f28f2 --- /dev/null +++ b/src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.cql @@ -0,0 +1,33 @@ +-- ----------------------------------------------------- +-- Table `server_configuration4` +-- ----------------------------------------------------- +CREATE TABLE server_configuration4 ( + config_id varchar, + timestamp bigint, + json_data varchar, + generic_data varchar, + PRIMARY KEY ((config_id)) +); + +-- ----------------------------------------------------- +-- Table `server_configuration6` +-- ----------------------------------------------------- +CREATE TABLE server_configuration6 ( + config_id varchar, + timestamp bigint, + json_data varchar, + generic_data varchar, + PRIMARY KEY ((config_id)) +); + +-- Finally, the version of the schema. We start at 1.0 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +CREATE TABLE config_schema_version ( + version int, + minor int, + PRIMARY KEY ((version)) +); + +INSERT INTO config_schema_version (version, minor) VALUES (1, 0); diff --git a/src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.sh.in b/src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.sh.in new file mode 100644 index 0000000000..4c3ac24595 --- /dev/null +++ b/src/share/database/scripts/cql/config_upgrade_0.0_to_1.0.sh.in @@ -0,0 +1,21 @@ +#!/bin/sh + +prefix=@prefix@ +# Include utilities. Use installed version if available and +# use build version if it isn't. +if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then + . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh +else + . @abs_top_builddir@/src/bin/admin/admin-utils.sh +fi + +VERSION=`cql_config_version "$@"` + +if [ "$VERSION" != "0.0" ]; then + printf "This script upgrades 0.0 to 1.0. Reported version is $VERSION. Skipping upgrade.\n" + exit 0 +fi + +cql_execute_script "$@" config_upgrade_0.0_to_1.0.cql + +exit $? diff --git a/src/share/database/scripts/cql/configdb_create.cql b/src/share/database/scripts/cql/configdb_create.cql new file mode 100644 index 0000000000..1e3e2f28f2 --- /dev/null +++ b/src/share/database/scripts/cql/configdb_create.cql @@ -0,0 +1,33 @@ +-- ----------------------------------------------------- +-- Table `server_configuration4` +-- ----------------------------------------------------- +CREATE TABLE server_configuration4 ( + config_id varchar, + timestamp bigint, + json_data varchar, + generic_data varchar, + PRIMARY KEY ((config_id)) +); + +-- ----------------------------------------------------- +-- Table `server_configuration6` +-- ----------------------------------------------------- +CREATE TABLE server_configuration6 ( + config_id varchar, + timestamp bigint, + json_data varchar, + generic_data varchar, + PRIMARY KEY ((config_id)) +); + +-- Finally, the version of the schema. We start at 1.0 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +CREATE TABLE config_schema_version ( + version int, + minor int, + PRIMARY KEY ((version)) +); + +INSERT INTO config_schema_version (version, minor) VALUES (1, 0); diff --git a/src/share/database/scripts/cql/dhcpdb_drop.cql b/src/share/database/scripts/cql/dhcpdb_drop.cql index 387451f561..b29538fef8 100644 --- a/src/share/database/scripts/cql/dhcpdb_drop.cql +++ b/src/share/database/scripts/cql/dhcpdb_drop.cql @@ -46,3 +46,8 @@ DROP INDEX IF EXISTS host_reservationsindex4; DROP INDEX IF EXISTS host_reservationsindex5; DROP INDEX IF EXISTS host_reservationsindex6; DROP INDEX IF EXISTS host_reservationsindex7; + +DROP TABLE IF EXISTS server_configuration4; +DROP TABLE IF EXISTS server_configuration6; +DROP TABLE IF EXISTS config_schema_version; +DROP TABLE IF EXISTS master_schema_version; diff --git a/src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.cql b/src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.cql new file mode 100644 index 0000000000..0f0d52a16f --- /dev/null +++ b/src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.cql @@ -0,0 +1,41 @@ +-- ----------------------------------------------------- +-- Table `server_configuration4` +-- ----------------------------------------------------- +CREATE TABLE server_configuration4 ( + instance_id varchar, + timestamp bigint, + config_database varchar, + config_database_name varchar, + server_config varchar, + PRIMARY KEY ((instance_id)) +); + +-- Create search indexes for server_configuration4 table +CREATE INDEX IF NOT EXISTS server_configuration4index1 ON server_configuration4 (config_database_name); + +-- ----------------------------------------------------- +-- Table `server_configuration6` +-- ----------------------------------------------------- +CREATE TABLE server_configuration6 ( + instance_id varchar, + timestamp bigint, + config_database varchar, + config_database_name varchar, + server_config varchar, + PRIMARY KEY ((instance_id)) +); + +-- Create search indexes for server_configuration6 table +CREATE INDEX IF NOT EXISTS server_configuration6index1 ON server_configuration6 (config_database_name); + +-- Finally, the version of the schema. We start at 1.0 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +CREATE TABLE master_schema_version ( + version int, + minor int, + PRIMARY KEY ((version)) +); + +INSERT INTO master_schema_version (version, minor) VALUES (1, 0); diff --git a/src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.sh.in b/src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.sh.in new file mode 100644 index 0000000000..4e36397d20 --- /dev/null +++ b/src/share/database/scripts/cql/master_upgrade_0.0_to_1.0.sh.in @@ -0,0 +1,23 @@ +#!/bin/sh + +prefix=@prefix@ +# Include utilities. Use installed version if available and +# use build version if it isn't. +if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then + . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh +else + . @abs_top_builddir@/src/bin/admin/admin-utils.sh +fi + +VERSION=`cql_master_version "$@"` + +if [ "$VERSION" != "0.0" ]; then + printf "This script upgrades 0.0 to 1.0. Reported version is $VERSION. Skipping upgrade.\n" + exit 0 +fi + +cql_execute_script "$@" master_upgrade_0.0_to_1.0.cql + +RESULT=$? + +exit $? diff --git a/src/share/database/scripts/cql/masterdb_create.cql b/src/share/database/scripts/cql/masterdb_create.cql new file mode 100644 index 0000000000..e9926599c8 --- /dev/null +++ b/src/share/database/scripts/cql/masterdb_create.cql @@ -0,0 +1,41 @@ +-- ----------------------------------------------------- +-- Table `server_configuration4` +-- ----------------------------------------------------- +CREATE TABLE server_configuration4 ( + instance_id varchar, + timestamp bigint, + config_database varchar, + config_database_name varchar, + server_config varchar, + PRIMARY KEY ((instance_id)) +); + +-- Create search indexes for server_configuration4 table +CREATE INDEX IF NOT EXISTS server_configuration4index1 ON server_configuration4 (config_database); + +-- ----------------------------------------------------- +-- Table `server_configuration6` +-- ----------------------------------------------------- +CREATE TABLE server_configuration6 ( + instance_id varchar, + timestamp bigint, + config_database varchar, + config_database_name varchar, + server_config varchar, + PRIMARY KEY ((instance_id)) +); + +-- Create search indexes for server_configuration6 table +CREATE INDEX IF NOT EXISTS server_configuration6index1 ON server_configuration6 (config_database); + +-- Finally, the version of the schema. We start at 1.0 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +CREATE TABLE master_schema_version ( + version int, + minor int, + PRIMARY KEY ((version)) +); + +INSERT INTO master_schema_version (version, minor) VALUES (1, 0); diff --git a/src/share/database/scripts/mysql/.gitignore b/src/share/database/scripts/mysql/.gitignore index 8f9f1ce2bc..2c634de768 100644 --- a/src/share/database/scripts/mysql/.gitignore +++ b/src/share/database/scripts/mysql/.gitignore @@ -6,3 +6,5 @@ /upgrade_5.0_to_5.1.sh /upgrade_5.1_to_5.2.sh /upgrade_5.2_to_6.0.sh +config_upgrade_0.0_to_1.0.sh +master_upgrade_0.0_to_1.0.sh diff --git a/src/share/database/scripts/mysql/Makefile.am b/src/share/database/scripts/mysql/Makefile.am index eff6631399..92fe98b433 100644 --- a/src/share/database/scripts/mysql/Makefile.am +++ b/src/share/database/scripts/mysql/Makefile.am @@ -1,8 +1,14 @@ SUBDIRS = . sqlscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts/mysql -sqlscripts_DATA = dhcpdb_create.mysql +sqlscripts_DATA = dhcpdb_create.mysql sqlscripts_DATA += dhcpdb_drop.mysql +sqlscripts_DATA += configdb_create.mysql +sqlscripts_DATA += config_upgrade_0.0_to_1.0.mysql +sqlscripts_DATA += config_upgrade_0.0_to_1.0.sh +sqlscripts_DATA += masterdb_create.mysql +sqlscripts_DATA += master_upgrade_0.0_to_1.0.mysql +sqlscripts_DATA += master_upgrade_0.0_to_1.0.sh sqlscripts_DATA += upgrade_1.0_to_2.0.sh sqlscripts_DATA += upgrade_2.0_to_3.0.sh sqlscripts_DATA += upgrade_3.0_to_4.0.sh diff --git a/src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.mysql b/src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.mysql new file mode 100644 index 0000000000..8fa86a1bb0 --- /dev/null +++ b/src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.mysql @@ -0,0 +1,34 @@ +# +# Create table holding server DCHPv4 configuration +# +CREATE TABLE IF NOT EXISTS server_configuration4 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP NOT NULL, + json_data LONGTEXT NOT NULL, + generic_data LONGTEXT NOT NULL, + PRIMARY KEY (config_id) +) ENGINE=InnoDB; + +# +# Create table holding server DCHPv6 configuration +# +CREATE TABLE IF NOT EXISTS server_configuration6 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP NOT NULL, + json_data LONGTEXT NOT NULL, + generic_data LONGTEXT NOT NULL, + PRIMARY KEY (config_id) +) ENGINE=InnoDB; + +# Finally, the version of the schema. We start at 1.0 during development. +# This table is only modified during schema upgrades. For historical reasons +# (related to the names of the columns in the BIND 10 DNS database file), the +# first column is called "version" and not "major". +CREATE TABLE IF NOT EXISTS config_schema_version ( + version INT PRIMARY KEY NOT NULL, # Major version number + minor INT # Minor version number + ) ENGINE = INNODB; + +# Update the schema version number +INSERT INTO config_schema_version VALUES (1, 0); +# This line concludes database upgrade to version 1.0. diff --git a/src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.sh.in b/src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.sh.in new file mode 100644 index 0000000000..052a71f14c --- /dev/null +++ b/src/share/database/scripts/mysql/config_upgrade_0.0_to_1.0.sh.in @@ -0,0 +1,21 @@ +#!/bin/sh + +prefix=@prefix@ +# Include utilities. Use installed version if available and +# use build version if it isn't. +if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then + . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh +else + . @abs_top_builddir@/src/bin/admin/admin-utils.sh +fi + +VERSION=`mysql_config_version "$@"` + +if [ "$VERSION" != "0.0" ]; then + printf "This script upgrades 0.0 to 1.0. Reported version is $VERSION. Skipping upgrade.\n" + exit 0 +fi + +mysql_execute_script "$@" config_upgrade_0.0_to_1.0.mysql + +exit $? diff --git a/src/share/database/scripts/mysql/configdb_create.mysql b/src/share/database/scripts/mysql/configdb_create.mysql new file mode 100644 index 0000000000..8fa86a1bb0 --- /dev/null +++ b/src/share/database/scripts/mysql/configdb_create.mysql @@ -0,0 +1,34 @@ +# +# Create table holding server DCHPv4 configuration +# +CREATE TABLE IF NOT EXISTS server_configuration4 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP NOT NULL, + json_data LONGTEXT NOT NULL, + generic_data LONGTEXT NOT NULL, + PRIMARY KEY (config_id) +) ENGINE=InnoDB; + +# +# Create table holding server DCHPv6 configuration +# +CREATE TABLE IF NOT EXISTS server_configuration6 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP NOT NULL, + json_data LONGTEXT NOT NULL, + generic_data LONGTEXT NOT NULL, + PRIMARY KEY (config_id) +) ENGINE=InnoDB; + +# Finally, the version of the schema. We start at 1.0 during development. +# This table is only modified during schema upgrades. For historical reasons +# (related to the names of the columns in the BIND 10 DNS database file), the +# first column is called "version" and not "major". +CREATE TABLE IF NOT EXISTS config_schema_version ( + version INT PRIMARY KEY NOT NULL, # Major version number + minor INT # Minor version number + ) ENGINE = INNODB; + +# Update the schema version number +INSERT INTO config_schema_version VALUES (1, 0); +# This line concludes database upgrade to version 1.0. diff --git a/src/share/database/scripts/mysql/dhcpdb_create.mysql b/src/share/database/scripts/mysql/dhcpdb_create.mysql index 09dd299b0a..fd0d1b4ac8 100644 --- a/src/share/database/scripts/mysql/dhcpdb_create.mysql +++ b/src/share/database/scripts/mysql/dhcpdb_create.mysql @@ -43,7 +43,6 @@ CREATE TABLE lease4 ( hostname VARCHAR(255) # The FQDN of the client ) ENGINE = INNODB; - # Create search indexes for lease4 table # index by hwaddr and subnet_id CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id); diff --git a/src/share/database/scripts/mysql/dhcpdb_drop.mysql b/src/share/database/scripts/mysql/dhcpdb_drop.mysql index 0f1e27688e..592d00fd45 100644 --- a/src/share/database/scripts/mysql/dhcpdb_drop.mysql +++ b/src/share/database/scripts/mysql/dhcpdb_drop.mysql @@ -31,3 +31,8 @@ DROP TRIGGER IF EXISTS lease6_stat_insert; DROP TRIGGER IF EXISTS lease6_stat_update; DROP TRIGGER IF EXISTS lease6_stat_delete; DROP TABLE IF EXISTS lease6_stat; + +DROP TABLE IF EXISTS server_configuration4; +DROP TABLE IF EXISTS server_configuration6; +DROP TABLE IF EXISTS config_schema_version; +DROP TABLE IF EXISTS master_schema_version; diff --git a/src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.mysql b/src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.mysql new file mode 100644 index 0000000000..a34f3b542a --- /dev/null +++ b/src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.mysql @@ -0,0 +1,36 @@ +# ----------------------------------------------------- +# Table `server_configuration4` +# ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS server_configuration4 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP NOT NULL, + server_config TEXT NOT NULL, + config_database TEXT NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id), + KEY `idx_server_configuration4_config_database_name` (`config_database_name`) +) ENGINE=InnoDB; + +# ----------------------------------------------------- +# Table `server_configuration6` +# ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS server_configuration6 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP NOT NULL, + server_config TEXT NOT NULL, + config_database TEXT NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id), + KEY `idx_server_configuration6_config_database_name` (`config_database_name`) +) ENGINE=InnoDB; + +# Finally, the version of the schema. We start at 1.0 during development. +# This table is only modified during schema upgrades. For historical reasons +# (related to the names of the columns in the BIND 10 DNS database file), the +# first column is called "version" and not "major". +CREATE TABLE IF NOT EXISTS master_schema_version ( + version int, + minor int, + PRIMARY KEY (version) +); +INSERT INTO master_schema_version (version, minor) VALUES (1, 0); diff --git a/src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.sh.in b/src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.sh.in new file mode 100755 index 0000000000..592faf66c8 --- /dev/null +++ b/src/share/database/scripts/mysql/master_upgrade_0.0_to_1.0.sh.in @@ -0,0 +1,21 @@ +#!/bin/sh + +prefix=@prefix@ +# Include utilities. Use installed version if available and +# use build version if it isn't. +if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then + . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh +else + . @abs_top_builddir@/src/bin/admin/admin-utils.sh +fi + +VERSION=`mysql_master_version "$@"` + +if [ "$VERSION" != "0.0" ]; then + printf "This script upgrades 0.0 to 1.0. Reported version is $VERSION. Skipping upgrade.\n" + exit 0 +fi + +mysql_execute_script "$@" master_upgrade_0.0_to_1.0.mysql + +exit $? diff --git a/src/share/database/scripts/mysql/masterdb_create.mysql b/src/share/database/scripts/mysql/masterdb_create.mysql new file mode 100644 index 0000000000..a34f3b542a --- /dev/null +++ b/src/share/database/scripts/mysql/masterdb_create.mysql @@ -0,0 +1,36 @@ +# ----------------------------------------------------- +# Table `server_configuration4` +# ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS server_configuration4 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP NOT NULL, + server_config TEXT NOT NULL, + config_database TEXT NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id), + KEY `idx_server_configuration4_config_database_name` (`config_database_name`) +) ENGINE=InnoDB; + +# ----------------------------------------------------- +# Table `server_configuration6` +# ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS server_configuration6 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP NOT NULL, + server_config TEXT NOT NULL, + config_database TEXT NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id), + KEY `idx_server_configuration6_config_database_name` (`config_database_name`) +) ENGINE=InnoDB; + +# Finally, the version of the schema. We start at 1.0 during development. +# This table is only modified during schema upgrades. For historical reasons +# (related to the names of the columns in the BIND 10 DNS database file), the +# first column is called "version" and not "major". +CREATE TABLE IF NOT EXISTS master_schema_version ( + version int, + minor int, + PRIMARY KEY (version) +); +INSERT INTO master_schema_version (version, minor) VALUES (1, 0); diff --git a/src/share/database/scripts/pgsql/.gitignore b/src/share/database/scripts/pgsql/.gitignore index f435980107..51a4e84cea 100644 --- a/src/share/database/scripts/pgsql/.gitignore +++ b/src/share/database/scripts/pgsql/.gitignore @@ -4,3 +4,5 @@ upgrade_3.0_to_3.1.sh upgrade_3.1_to_3.2.sh upgrade_3.2_to_3.3.sh upgrade_3.3_to_4.0.sh +config_upgrade_0.0_to_1.0.sh +master_upgrade_0.0_to_1.0.sh diff --git a/src/share/database/scripts/pgsql/Makefile.am b/src/share/database/scripts/pgsql/Makefile.am index 7187d14397..a8a2e63384 100644 --- a/src/share/database/scripts/pgsql/Makefile.am +++ b/src/share/database/scripts/pgsql/Makefile.am @@ -1,8 +1,14 @@ SUBDIRS = . sqlscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts/pgsql -sqlscripts_DATA = dhcpdb_create.pgsql +sqlscripts_DATA = dhcpdb_create.pgsql sqlscripts_DATA += dhcpdb_drop.pgsql +sqlscripts_DATA += configdb_create.pgsql +sqlscripts_DATA += config_upgrade_0.0_to_1.0.pgsql +sqlscripts_DATA += config_upgrade_0.0_to_1.0.sh +sqlscripts_DATA += masterdb_create.pgsql +sqlscripts_DATA += master_upgrade_0.0_to_1.0.pgsql +sqlscripts_DATA += master_upgrade_0.0_to_1.0.sh sqlscripts_DATA += upgrade_1.0_to_2.0.sh sqlscripts_DATA += upgrade_2.0_to_3.0.sh sqlscripts_DATA += upgrade_3.0_to_3.1.sh diff --git a/src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.pgsql b/src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.pgsql new file mode 100644 index 0000000000..b2e33212a5 --- /dev/null +++ b/src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.pgsql @@ -0,0 +1,41 @@ +START TRANSACTION; + +-- Upgrade to schema 0.0 begins here: + +-- +-- Table structure for DHCPv4 server configuration +-- +CREATE TABLE IF NOT EXISTS server_configuration4 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + json_data BYTEA NOT NULL, + generic_data BYTEA NOT NULL, + PRIMARY KEY (config_id) +); + +-- +-- Table structure for DHCPv6 server configuration +-- +CREATE TABLE IF NOT EXISTS server_configuration6 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + json_data BYTEA NOT NULL, + generic_data BYTEA NOT NULL, + PRIMARY KEY (config_id) +); + +-- Finally, the version of the schema. We start at 0.1 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +CREATE TABLE IF NOT EXISTS config_schema_version ( + version INT PRIMARY KEY NOT NULL, -- Major version number + minor INT -- Minor version number + ); + +-- Set 1.0 schema version. +INSERT INTO config_schema_version VALUES (1, 0); +-- Schema 1.0 specification ends here. + +-- Commit the script transaction +COMMIT; diff --git a/src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.sh.in b/src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.sh.in new file mode 100644 index 0000000000..523a7e19ab --- /dev/null +++ b/src/share/database/scripts/pgsql/config_upgrade_0.0_to_1.0.sh.in @@ -0,0 +1,21 @@ +#!/bin/sh + +prefix=@prefix@ +# Include utilities. Use installed version if available and +# use build version if it isn't. +if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then + . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh +else + . @abs_top_builddir@/src/bin/admin/admin-utils.sh +fi + +VERSION=`pgsql_config_version "$@"` + +if [ "$VERSION" != "0.0" ]; then + printf "This script upgrades 0.0 to 1.0. Reported version is $VERSION. Skipping upgrade.\n" + exit 0 +fi + +pgsql_execute_script "$@" config_upgrade_0.0_to_1.0.pgsql + +exit $? diff --git a/src/share/database/scripts/pgsql/configdb_create.pgsql b/src/share/database/scripts/pgsql/configdb_create.pgsql new file mode 100644 index 0000000000..b2e33212a5 --- /dev/null +++ b/src/share/database/scripts/pgsql/configdb_create.pgsql @@ -0,0 +1,41 @@ +START TRANSACTION; + +-- Upgrade to schema 0.0 begins here: + +-- +-- Table structure for DHCPv4 server configuration +-- +CREATE TABLE IF NOT EXISTS server_configuration4 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + json_data BYTEA NOT NULL, + generic_data BYTEA NOT NULL, + PRIMARY KEY (config_id) +); + +-- +-- Table structure for DHCPv6 server configuration +-- +CREATE TABLE IF NOT EXISTS server_configuration6 ( + config_id VARCHAR(36) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + json_data BYTEA NOT NULL, + generic_data BYTEA NOT NULL, + PRIMARY KEY (config_id) +); + +-- Finally, the version of the schema. We start at 0.1 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +CREATE TABLE IF NOT EXISTS config_schema_version ( + version INT PRIMARY KEY NOT NULL, -- Major version number + minor INT -- Minor version number + ); + +-- Set 1.0 schema version. +INSERT INTO config_schema_version VALUES (1, 0); +-- Schema 1.0 specification ends here. + +-- Commit the script transaction +COMMIT; diff --git a/src/share/database/scripts/pgsql/dhcpdb_drop.pgsql b/src/share/database/scripts/pgsql/dhcpdb_drop.pgsql index 3d302f0c4d..babbea3494 100644 --- a/src/share/database/scripts/pgsql/dhcpdb_drop.pgsql +++ b/src/share/database/scripts/pgsql/dhcpdb_drop.pgsql @@ -28,3 +28,8 @@ DROP TABLE IF EXISTS lease6_stat CASCADE; DROP FUNCTION IF EXISTS proc_stat_lease6_insert (); DROP FUNCTION IF EXISTS proc_stat_lease6_update (); DROP FUNCTION IF EXISTS proc_stat_lease6_delete (); + +DROP TABLE IF EXISTS server_configuration4 CASCADE; +DROP TABLE IF EXISTS server_configuration6 CASCADE; +DROP TABLE IF EXISTS config_schema_version CASCADE; +DROP TABLE IF EXISTS master_schema_version CASCADE; diff --git a/src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.pgsql b/src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.pgsql new file mode 100644 index 0000000000..cb37d92e6c --- /dev/null +++ b/src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.pgsql @@ -0,0 +1,41 @@ +-- All or nothing. Table creation should be atomic. +START TRANSACTION; + +-- Table structure for DHCPv4 server configuration +DROP TABLE IF EXISTS server_configuration4; +CREATE TABLE server_configuration4 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + server_config BYTEA NOT NULL, + config_database BYTEA NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id) +); + +-- Table structure for DHCPv6 server configuration +DROP TABLE IF EXISTS server_configuration6; +CREATE TABLE server_configuration6 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + server_config BYTEA NOT NULL, + config_database BYTEA NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id) +); + +-- Finally, the version of the schema. We start at 0.1 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +DROP TABLE IF EXISTS master_schema_version;; +CREATE TABLE master_schema_version ( + version INT PRIMARY KEY NOT NULL, + minor INT +); + +-- Set 1.0 schema version. +INSERT INTO master_schema_version VALUES (1, 0); +-- Schema 1.0 specification ends here. + +-- Commit the script transaction +COMMIT; diff --git a/src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.sh.in b/src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.sh.in new file mode 100644 index 0000000000..e92c9803d9 --- /dev/null +++ b/src/share/database/scripts/pgsql/master_upgrade_0.0_to_1.0.sh.in @@ -0,0 +1,21 @@ +#!/bin/sh + +prefix=@prefix@ +# Include utilities. Use installed version if available and +# use build version if it isn't. +if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then + . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh +else + . @abs_top_builddir@/src/bin/admin/admin-utils.sh +fi + +VERSION=`pgsql_master_version "$@"` + +if [ "$VERSION" != "0.0" ]; then + printf "This script upgrades 0.0 to 1.0. Reported version is $VERSION. Skipping upgrade.\n" + exit 0 +fi + +pgsql_execute_script "$@" master_upgrade_0.0_to_1.0.pgsql + +exit $? diff --git a/src/share/database/scripts/pgsql/masterdb_create.pgsql b/src/share/database/scripts/pgsql/masterdb_create.pgsql new file mode 100644 index 0000000000..cb37d92e6c --- /dev/null +++ b/src/share/database/scripts/pgsql/masterdb_create.pgsql @@ -0,0 +1,41 @@ +-- All or nothing. Table creation should be atomic. +START TRANSACTION; + +-- Table structure for DHCPv4 server configuration +DROP TABLE IF EXISTS server_configuration4; +CREATE TABLE server_configuration4 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + server_config BYTEA NOT NULL, + config_database BYTEA NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id) +); + +-- Table structure for DHCPv6 server configuration +DROP TABLE IF EXISTS server_configuration6; +CREATE TABLE server_configuration6 ( + instance_id VARCHAR(128) NOT NULL, + timestamp TIMESTAMP WITH TIME ZONE NOT NULL, + server_config BYTEA NOT NULL, + config_database BYTEA NOT NULL, + config_database_name VARCHAR(128) NOT NULL, + PRIMARY KEY (instance_id) +); + +-- Finally, the version of the schema. We start at 0.1 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +DROP TABLE IF EXISTS master_schema_version;; +CREATE TABLE master_schema_version ( + version INT PRIMARY KEY NOT NULL, + minor INT +); + +-- Set 1.0 schema version. +INSERT INTO master_schema_version VALUES (1, 0); +-- Schema 1.0 specification ends here. + +-- Commit the script transaction +COMMIT; From 1d378c5df2e7446ec1417a3d8f5d966e72b1934f Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Thu, 24 May 2018 22:07:59 +0300 Subject: [PATCH 02/29] removed defines --- src/lib/dhcpsrv/cql_srv_config_master_mgr.cc | 149 ------------------- src/lib/dhcpsrv/cql_srv_config_mgr.cc | 71 --------- 2 files changed, 220 deletions(-) diff --git a/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc index 27731b1da0..e62b2aee85 100644 --- a/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc +++ b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc @@ -159,11 +159,7 @@ StatementMap CqlMasterConfigExchange::tagged_statements_ = { "INSERT INTO server_configuration4 " "(instance_id, timestamp, server_config, config_database, config_database_name) " "VALUES (?, ?, ?, ?, ?) " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#else // TERASTREAM_FULL_TRANSACTIONS "IF NOT EXISTS " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {INSERT_SERVER_CONFIG6, // @@ -171,159 +167,87 @@ StatementMap CqlMasterConfigExchange::tagged_statements_ = { "INSERT INTO server_configuration6 " "(instance_id, timestamp, server_config, config_database, config_database_name) " "VALUES (?, ?, ?, ?, ?) " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#else // TERASTREAM_FULL_TRANSACTIONS "IF NOT EXISTS " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_CONFIGURATION4_BY_SRV_ID, // {GET_CONFIGURATION4_BY_SRV_ID, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration4 " "WHERE instance_id = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_CONFIGURATION6_BY_SRV_ID, // {GET_CONFIGURATION6_BY_SRV_ID, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration6 " "WHERE instance_id = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_CONFIGURATION4_BY_SHARD_DB, // {GET_CONFIGURATION4_BY_SHARD_DB, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration4 " "WHERE config_database_name = ? ALLOW FILTERING " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_CONFIGURATION6_BY_SHARD_DB, // {GET_CONFIGURATION6_BY_SHARD_DB, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration6 " "WHERE config_database_name = ? ALLOW FILTERING " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_CONFIGURATION4_TIMESTAMP, // {GET_CONFIGURATION4_TIMESTAMP, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id, timestamp " "FROM server_configuration4 " "WHERE instance_id = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_CONFIGURATION6_TIMESTAMP, // {GET_CONFIGURATION6_TIMESTAMP, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id, timestamp " "FROM server_configuration6 " "WHERE instance_id = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_SERVERS_CONFIG4, // {GET_SERVERS_CONFIG4, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id " "FROM server_configuration4 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_SERVERS_CONFIG6, // {GET_SERVERS_CONFIG6, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT instance_id " "FROM server_configuration6 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {DELETE_SERVER_CONFIG4, // {DELETE_SERVER_CONFIG4, // "DELETE FROM server_configuration4 " "WHERE instance_id = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#else // TERASTREAM_FULL_TRANSACTIONS "IF EXISTS " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {DELETE_SERVER_CONFIG6, // {DELETE_SERVER_CONFIG6, // "DELETE FROM server_configuration6 " "WHERE instance_id = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#else // TERASTREAM_FULL_TRANSACTIONS "IF EXISTS " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_SERVERS_CONFIG4_SHARDS_NAME, // {GET_SERVERS_CONFIG4_SHARDS_NAME, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_database_name " "FROM server_configuration4 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, {GET_SERVERS_CONFIG6_SHARDS_NAME, // {GET_SERVERS_CONFIG6_SHARDS_NAME, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_database_name " "FROM server_configuration6 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }} // }; @@ -397,11 +321,6 @@ bool CqlMasterConfigExchange::insertCommon(CqlConnection& connection, assigned_values.add(const_cast(&config_database)); assigned_values.add(const_cast(&config_database_name)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = connection.getTransactionId(); - assigned_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - executeMutation(connection, assigned_values, statement_tag); return true; @@ -410,16 +329,8 @@ bool CqlMasterConfigExchange::insertCommon(CqlConnection& connection, CqlSrvConfigMasterMgr::CqlSrvConfigMasterMgr(const DatabaseConnection::ParameterMap& parameters) : SrvConfigMasterMgr(), dbconn_(parameters) { dbconn_.openDatabase(); -#ifdef TERASTREAM_FULL_TRANSACTIONS - dbconn_.setTransactionOperations(CqlTransactionExchange::BEGIN_TXN, - CqlTransactionExchange::COMMIT_TXN, - CqlTransactionExchange::ROLLBACK_TXN); -#endif // TERASTREAM_FULL_TRANSACTIONS dbconn_.prepareStatements(CqlMasterConfigExchange::tagged_statements_); dbconn_.prepareStatements(CqlMasterConfigVersionExchange::tagged_statements_); -#ifdef TERASTREAM_FULL_TRANSACTIONS - dbconn_.prepareStatements(CqlTransactionExchange::tagged_statements_); -#endif // TERASTREAM_FULL_TRANSACTIONS } CqlSrvConfigMasterMgr::~CqlSrvConfigMasterMgr() { @@ -483,11 +394,6 @@ SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig4(const std::string& inst AnyArray where_values; where_values.add(const_cast(&instance_id)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - where_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SRV_ID); @@ -514,11 +420,6 @@ SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig6(const std::string& inst AnyArray where_values; where_values.add(const_cast(&instance_id)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - where_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SRV_ID); @@ -549,11 +450,6 @@ bool CqlSrvConfigMasterMgr::getConfig4( AnyArray where_values; where_values.add(const_cast(&config_database_name)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - where_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SHARD_DB, false); @@ -581,11 +477,6 @@ bool CqlSrvConfigMasterMgr::getConfig6( AnyArray where_values; where_values.add(const_cast(&config_database_name)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - where_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SHARD_DB, false); @@ -613,11 +504,6 @@ CqlSrvConfigMasterMgr::getMasterConfig4Timestamp(const std::string& instance_id) where_values.add(const_cast(&instance_id)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - where_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_TIMESTAMP); @@ -647,11 +533,6 @@ CqlSrvConfigMasterMgr::getMasterConfig6Timestamp(const std::string& instance_id) where_values.add(const_cast(&instance_id)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - where_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_TIMESTAMP); @@ -713,11 +594,6 @@ bool CqlSrvConfigMasterMgr::getServersConfig4ShardsName(std::set& s AnyArray data; -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - data.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG4_SHARDS_NAME, false); @@ -744,11 +620,6 @@ bool CqlSrvConfigMasterMgr::getServersConfig6ShardsName(std::set& s AnyArray data; -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - data.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG6_SHARDS_NAME, false); @@ -812,11 +683,6 @@ bool CqlSrvConfigMasterMgr::getServersConfig4(std::vector& servers_ AnyArray data; -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - data.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); AnyArray collection = master_config_exchange->executeSelect( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG4); @@ -845,11 +711,6 @@ bool CqlSrvConfigMasterMgr::getServersConfig6(std::vector& servers_ AnyArray data; -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - data.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); AnyArray collection = master_config_exchange->executeSelect( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG6); @@ -879,11 +740,6 @@ bool CqlSrvConfigMasterMgr::deleteServerConfig4(const std::string& instance_id) AnyArray data; data.add(const_cast(&instance_id)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - data.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); master_config_exchange->executeMutation(dbconn_, data, CqlMasterConfigExchange::DELETE_SERVER_CONFIG4); @@ -904,11 +760,6 @@ bool CqlSrvConfigMasterMgr::deleteServerConfig6(const std::string& instance_id) AnyArray data; data.add(const_cast(&instance_id)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = dbconn_.getTransactionId(); - data.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); master_config_exchange->executeMutation(dbconn_, data, CqlMasterConfigExchange::DELETE_SERVER_CONFIG6); diff --git a/src/lib/dhcpsrv/cql_srv_config_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_mgr.cc index bf4b1f9fb2..b8b05dd36f 100644 --- a/src/lib/dhcpsrv/cql_srv_config_mgr.cc +++ b/src/lib/dhcpsrv/cql_srv_config_mgr.cc @@ -180,84 +180,48 @@ constexpr StatementTag CqlConfigExchange::UPDATE_CONFIGURATION6; StatementMap CqlConfigExchange::tagged_statements_ = { {GET_CONFIGURATION4_TIMESTAMP, // {GET_CONFIGURATION4_TIMESTAMP, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_id, timestamp " "FROM server_configuration4 " "LIMIT 1 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // GET_CONFIGURATION6_TIMESTAMP {GET_CONFIGURATION6_TIMESTAMP, // {GET_CONFIGURATION6_TIMESTAMP, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_id, timestamp " "FROM server_configuration6 " "LIMIT 1 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // GET_GENERIC_CONFIGURATION4 {GET_GENERIC_CONFIGURATION4, // {GET_GENERIC_CONFIGURATION4, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_id, timestamp, generic_data " "FROM server_configuration4 " "LIMIT 1 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // GET_GENERIC_CONFIGURATION6 {GET_GENERIC_CONFIGURATION6, // {GET_GENERIC_CONFIGURATION6, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_id, timestamp, generic_data " "FROM server_configuration6 LIMIT 1 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // GET_JSON_CONFIGURATION4 {GET_JSON_CONFIGURATION4, // {GET_JSON_CONFIGURATION4, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_id, timestamp, json_data " "FROM server_configuration4 " "LIMIT 1 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // GET_JSON_CONFIGURATION6 {GET_JSON_CONFIGURATION6, // {GET_JSON_CONFIGURATION6, // -#ifdef TERASTREAM_FULL_TRANSACTIONS - "@free = " -#endif // TERASTREAM_FULL_TRANSACTIONS "SELECT config_id, timestamp, json_data " "FROM server_configuration6 " "LIMIT 1 " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // INSERT_CONFIGURATION4 @@ -266,9 +230,6 @@ StatementMap CqlConfigExchange::tagged_statements_ = { "INSERT INTO server_configuration4 " "(config_id, timestamp, json_data, generic_data) " "VALUES (?, ?, ?, ?) IF NOT EXISTS " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // INSERT_CONFIGURATION6 @@ -277,9 +238,6 @@ StatementMap CqlConfigExchange::tagged_statements_ = { "INSERT INTO server_configuration6 " "(config_id, timestamp, json_data, generic_data) " "VALUES (?, ?, ?, ?) IF NOT EXISTS " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // UPDATE_CONFIGURATION4 @@ -289,9 +247,6 @@ StatementMap CqlConfigExchange::tagged_statements_ = { "SET timestamp = ?, json_data = ?, " "generic_data = ? WHERE config_id = ? " "IF timestamp = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }}, // UPDATE_CONFIGURATION6 @@ -301,9 +256,6 @@ StatementMap CqlConfigExchange::tagged_statements_ = { "SET timestamp = ?, json_data = ?, " "generic_data = ? WHERE config_id = ? " "IF timestamp = ? " -#ifdef TERASTREAM_FULL_TRANSACTIONS - "IN TXN ? " -#endif // TERASTREAM_FULL_TRANSACTIONS }} // }; @@ -351,11 +303,6 @@ CqlConfigExchange::getCommon(CqlConnection& connection, // Bind to array. AnyArray where_values; -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = connection.getTransactionId(); - where_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - AnyArray collection = executeSelect(connection, where_values, statement_tag, true); @@ -389,11 +336,6 @@ CqlConfigExchange::insertCommon(CqlConnection& connection, assigned_values.add(const_cast(&json_data)); assigned_values.add(const_cast(&generic_data)); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = connection.getTransactionId(); - assigned_values.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - executeMutation(connection, assigned_values, statement_tag); return true; @@ -420,11 +362,6 @@ CqlConfigExchange::updateCommon(CqlConnection& connection, data.add(const_cast(&config_id)); data.add(&old_config_timestamp); -#ifdef TERASTREAM_FULL_TRANSACTIONS - CassUuid txid = connection.getTransactionId(); - data.add(&txid); -#endif // TERASTREAM_FULL_TRANSACTIONS - executeMutation(connection, data, statement_tag); return true; @@ -434,16 +371,8 @@ CqlSrvConfigMgr::CqlSrvConfigMgr( const DatabaseConnection::ParameterMap& parameters) : SrvConfigMgr(), dbconn_(parameters) { dbconn_.openDatabase(); -#ifdef TERASTREAM_FULL_TRANSACTIONS - dbconn_.setTransactionOperations(CqlTransactionExchange::BEGIN_TXN, - CqlTransactionExchange::COMMIT_TXN, - CqlTransactionExchange::ROLLBACK_TXN); -#endif // TERASTREAM_FULL_TRANSACTIONS dbconn_.prepareStatements(CqlConfigExchange::tagged_statements_); dbconn_.prepareStatements(CqlConfigVersionExchange::tagged_statements_); -#ifdef TERASTREAM_FULL_TRANSACTIONS - dbconn_.prepareStatements(CqlTransactionExchange::tagged_statements_); -#endif // TERASTREAM_FULL_TRANSACTIONS } CqlSrvConfigMgr::~CqlSrvConfigMgr() { From dba34e813e827bcb330223a7b9bb398bccfbc794 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Thu, 24 May 2018 23:01:56 +0300 Subject: [PATCH 03/29] fixed compilation --- src/lib/dhcp/classify.h | 1 + src/lib/dhcpsrv/cql_srv_config_master_mgr.cc | 113 ------------------- src/lib/dhcpsrv/cql_srv_config_mgr.cc | 65 ----------- src/lib/dhcpsrv/dhcpsrv_messages.mes | 3 + src/lib/dhcpsrv/memfile_lease_mgr.h | 6 + 5 files changed, 10 insertions(+), 178 deletions(-) diff --git a/src/lib/dhcp/classify.h b/src/lib/dhcp/classify.h index 2ddf72e49f..d13b878367 100644 --- a/src/lib/dhcp/classify.h +++ b/src/lib/dhcp/classify.h @@ -7,6 +7,7 @@ #ifndef CLASSIFY_H #define CLASSIFY_H +#include #include #include #include diff --git a/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc index e62b2aee85..1e29d4ddc3 100644 --- a/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc +++ b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc @@ -19,7 +19,6 @@ #include -#include #include #include @@ -343,19 +342,11 @@ bool CqlSrvConfigMasterMgr::addServerConfig4(const std::string& instance_id, LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG4) .arg(instance_id) .arg(config_database_name); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); master_config_exchange->insertCommon(dbconn_, instance_id, server_config, config_database, config_database_name, CqlMasterConfigExchange::INSERT_SERVER_CONFIG4); - transaction.commit(); - return true; } @@ -366,31 +357,17 @@ bool CqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG6) .arg(instance_id) .arg(config_database_name); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); master_config_exchange->insertCommon(dbconn_, instance_id, server_config, config_database, config_database_name, CqlMasterConfigExchange::INSERT_SERVER_CONFIG6); - transaction.commit(); - return true; } SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig4(const std::string& instance_id) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4) .arg(instance_id); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray where_values; where_values.add(const_cast(&instance_id)); @@ -398,8 +375,6 @@ SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig4(const std::string& inst SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SRV_ID); - transaction.commit(); - SrvConfigMasterInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -411,12 +386,6 @@ SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig4(const std::string& inst SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig6(const std::string& instance_id) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6) .arg(instance_id); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray where_values; where_values.add(const_cast(&instance_id)); @@ -424,8 +393,6 @@ SrvConfigMasterInfoPtr CqlSrvConfigMasterMgr::getConfig6(const std::string& inst SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SRV_ID); - transaction.commit(); - SrvConfigMasterInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -442,11 +409,6 @@ bool CqlSrvConfigMasterMgr::getConfig4( server_configurations.clear(); - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray where_values; where_values.add(const_cast(&config_database_name)); @@ -454,8 +416,6 @@ bool CqlSrvConfigMasterMgr::getConfig4( SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_BY_SHARD_DB, false); - transaction.commit(); - server_configurations = collection_data; return true; @@ -469,11 +429,6 @@ bool CqlSrvConfigMasterMgr::getConfig6( server_configurations.clear(); - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray where_values; where_values.add(const_cast(&config_database_name)); @@ -481,8 +436,6 @@ bool CqlSrvConfigMasterMgr::getConfig6( SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_BY_SHARD_DB, false); - transaction.commit(); - server_configurations = collection_data; return true; @@ -495,11 +448,6 @@ CqlSrvConfigMasterMgr::getMasterConfig4Timestamp(const std::string& instance_id) DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP) .arg(instance_id); - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray where_values; where_values.add(const_cast(&instance_id)); @@ -508,8 +456,6 @@ CqlSrvConfigMasterMgr::getMasterConfig4Timestamp(const std::string& instance_id) SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION4_TIMESTAMP); - transaction.commit(); - SrvConfigMasterInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -524,11 +470,6 @@ CqlSrvConfigMasterMgr::getMasterConfig6Timestamp(const std::string& instance_id) DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP) .arg(instance_id); - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray where_values; where_values.add(const_cast(&instance_id)); @@ -537,8 +478,6 @@ CqlSrvConfigMasterMgr::getMasterConfig6Timestamp(const std::string& instance_id) SrvConfigMasterInfoCollection collection = master_config_exchange->getCommon( dbconn_, where_values, CqlMasterConfigExchange::GET_CONFIGURATION6_TIMESTAMP); - transaction.commit(); - SrvConfigMasterInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -550,34 +489,24 @@ CqlSrvConfigMasterMgr::getMasterConfig6Timestamp(const std::string& instance_id) bool CqlSrvConfigMasterMgr::clearServersConfig4() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG4); - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::vector servers_id; getServersConfig4(servers_id); for (std::string const& server_id : servers_id) { deleteServerConfig4(server_id); } - transaction.commit(); - return true; } bool CqlSrvConfigMasterMgr::clearServersConfig6() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG6); - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::vector servers_id; getServersConfig6(servers_id); for (std::string const& server_id : servers_id) { deleteServerConfig6(server_id); } - transaction.commit(); - return true; } @@ -587,19 +516,12 @@ bool CqlSrvConfigMasterMgr::getServersConfig4ShardsName(std::set& s shards_list.clear(); - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray data; std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG4_SHARDS_NAME, false); - transaction.commit(); - for (SrvConfigMasterInfoPtr const& srvInfo : collection_data) { shards_list.insert(srvInfo->config_database_name_); } @@ -613,19 +535,12 @@ bool CqlSrvConfigMasterMgr::getServersConfig6ShardsName(std::set& s shards_list.clear(); - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray data; std::unique_ptr master_config_exchange(new CqlMasterConfigExchange()); SrvConfigMasterInfoCollection collection_data = master_config_exchange->getCommon( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG6_SHARDS_NAME, false); - transaction.commit(); - for (SrvConfigMasterInfoPtr const& srvInfo : collection_data) { shards_list.insert(srvInfo->config_database_name_); } @@ -674,12 +589,7 @@ void CqlSrvConfigMasterMgr::rollback() { // Protected methods bool CqlSrvConfigMasterMgr::getServersConfig4(std::vector& servers_id) const { - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - servers_id.clear(); - CqlTransaction transaction(dbconn_); AnyArray data; @@ -687,8 +597,6 @@ bool CqlSrvConfigMasterMgr::getServersConfig4(std::vector& servers_ AnyArray collection = master_config_exchange->executeSelect( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG4); - transaction.commit(); - if (collection.empty()) { return true; } @@ -702,12 +610,7 @@ bool CqlSrvConfigMasterMgr::getServersConfig4(std::vector& servers_ } bool CqlSrvConfigMasterMgr::getServersConfig6(std::vector& servers_id) const { - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - servers_id.clear(); - CqlTransaction transaction(dbconn_); AnyArray data; @@ -715,8 +618,6 @@ bool CqlSrvConfigMasterMgr::getServersConfig6(std::vector& servers_ AnyArray collection = master_config_exchange->executeSelect( dbconn_, data, CqlMasterConfigExchange::GET_SERVERS_CONFIG6); - transaction.commit(); - if (collection.empty()) { return true; } @@ -732,11 +633,6 @@ bool CqlSrvConfigMasterMgr::getServersConfig6(std::vector& servers_ bool CqlSrvConfigMasterMgr::deleteServerConfig4(const std::string& instance_id) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG4) .arg(instance_id); - - // CqlTransaction rolls back the transaction on its destructor. - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray data; data.add(const_cast(&instance_id)); @@ -744,19 +640,12 @@ bool CqlSrvConfigMasterMgr::deleteServerConfig4(const std::string& instance_id) master_config_exchange->executeMutation(dbconn_, data, CqlMasterConfigExchange::DELETE_SERVER_CONFIG4); - transaction.commit(); - return true; } bool CqlSrvConfigMasterMgr::deleteServerConfig6(const std::string& instance_id) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG6) .arg(instance_id); - - // CqlTransaction rolls back the transaction on its destructor. - // Begin the transaction. - CqlTransaction transaction(dbconn_); - AnyArray data; data.add(const_cast(&instance_id)); @@ -764,8 +653,6 @@ bool CqlSrvConfigMasterMgr::deleteServerConfig6(const std::string& instance_id) master_config_exchange->executeMutation(dbconn_, data, CqlMasterConfigExchange::DELETE_SERVER_CONFIG6); - transaction.commit(); - return true; } diff --git a/src/lib/dhcpsrv/cql_srv_config_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_mgr.cc index b8b05dd36f..32086331da 100644 --- a/src/lib/dhcpsrv/cql_srv_config_mgr.cc +++ b/src/lib/dhcpsrv/cql_srv_config_mgr.cc @@ -19,7 +19,6 @@ #include -#include #include #include // for NULL @@ -382,18 +381,10 @@ SrvConfigInfoPtr CqlSrvConfigMgr::getConfig4Timestamp() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG4_TIMESTAMP); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr config_exchange(new CqlConfigExchange()); SrvConfigInfoCollection collection = config_exchange->getCommon( dbconn_, CqlConfigExchange::GET_CONFIGURATION4_TIMESTAMP); - transaction.commit(); - SrvConfigInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -406,18 +397,10 @@ SrvConfigInfoPtr CqlSrvConfigMgr::getConfig6Timestamp() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG6_TIMESTAMP); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr config_exchange(new CqlConfigExchange()); SrvConfigInfoCollection collection = config_exchange->getCommon( dbconn_, CqlConfigExchange::GET_CONFIGURATION6_TIMESTAMP); - transaction.commit(); - SrvConfigInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -430,18 +413,10 @@ SrvConfigInfoPtr CqlSrvConfigMgr::getGenericConfig4() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG4_GENERIC); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr config_exchange(new CqlConfigExchange()); SrvConfigInfoCollection collection = config_exchange->getCommon( dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION4); - transaction.commit(); - SrvConfigInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -454,18 +429,10 @@ SrvConfigInfoPtr CqlSrvConfigMgr::getGenericConfig6() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG6_GENERIC); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr config_exchange(new CqlConfigExchange()); SrvConfigInfoCollection collection = config_exchange->getCommon( dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION6); - transaction.commit(); - SrvConfigInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -478,18 +445,10 @@ SrvConfigInfoPtr CqlSrvConfigMgr::getJsonConfig4() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG4_JSON); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr config_exchange(new CqlConfigExchange()); SrvConfigInfoCollection collection = config_exchange->getCommon( dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION4); - transaction.commit(); - SrvConfigInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -502,18 +461,10 @@ SrvConfigInfoPtr CqlSrvConfigMgr::getJsonConfig6() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG6_JSON); - - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - std::unique_ptr config_exchange(new CqlConfigExchange()); SrvConfigInfoCollection collection = config_exchange->getCommon( dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION6); - transaction.commit(); - SrvConfigInfoPtr result; if (!collection.empty()) { result = *collection.begin(); @@ -566,16 +517,10 @@ CqlSrvConfigMgr::updateConfig4(const int64_t config_timestamp, // also using transactions because we don't want another user to insert also // a new configuration in the same time. - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - SrvConfigInfoPtr current_config = getConfig4Timestamp(); if (!current_config) { if (insertConfig4(json_data, generic_data)) { - transaction.commit(); return true; } return false; @@ -594,8 +539,6 @@ CqlSrvConfigMgr::updateConfig4(const int64_t config_timestamp, return false; } - transaction.commit(); - return true; } @@ -617,16 +560,10 @@ CqlSrvConfigMgr::updateConfig6(const int64_t config_timestamp, // also using transactions because we don't want another user to insert also // a new configuration in the same time. - // CqlTransaction rolls back the transaction on its destructor. - - // Begin the transaction. - CqlTransaction transaction(dbconn_); - SrvConfigInfoPtr current_config = getConfig6Timestamp(); if (!current_config) { if (insertConfig6(json_data, generic_data)) { - transaction.commit(); return true; } return false; @@ -644,8 +581,6 @@ CqlSrvConfigMgr::updateConfig6(const int64_t config_timestamp, return false; } - transaction.commit(); - return true; } diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index b9b286a2d1..cdb5035ce9 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -200,6 +200,9 @@ The server has issued a begin transaction call. % DHCPSRV_CQL_CONNECTION_COMMIT committing to Cassandra database on current connection. A commit call been issued on the server. For Cassandra, this is a no-op. +% DHCPSRV_CQL_BEGIN_TRANSACTION begin transaction. +The server has issued a begin transaction call. + % DHCPSRV_CQL_CONNECTION_ROLLBACK rolling back Cassandra database on current connection. The code has issued a rollback call. For Cassandra, this is a no-op. diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index c7b2a4d317..9fa2eecf64 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -443,6 +443,12 @@ class Memfile_LeaseMgr : public LeaseMgr { return (std::make_pair(MAJOR_VERSION, MINOR_VERSION)); } + /// @brief Start Transaction + /// + /// Start transaction for database operations. On databases that don't + /// support transactions, this is a no-op. + virtual bool startTransaction(); + /// @brief Commit Transactions /// /// Commits all pending database operations. On databases that don't From b6a93918dcc1ed820f502bb0fc4927ffd3910a6e Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 25 May 2018 14:19:05 +0300 Subject: [PATCH 04/29] minor changes --- doc/examples/kea4/cassandra.json | 4 + doc/examples/kea6/backends.json | 3 +- doc/examples/kea6/cassandra.json | 4 + doc/guide/dhcp4-srv.xml | 1 + doc/guide/dhcp6-srv.xml | 2 +- src/bin/admin/kea-admin.xml | 29 +++ src/bin/agent/Makefile.am | 3 +- src/bin/d2/Makefile.am | 1 + src/bin/dhcp4/ctrl_dhcp4_srv.cc | 168 +++++++++--------- src/bin/dhcp4/ctrl_dhcp4_srv.h | 12 +- src/bin/dhcp4/dhcp4_lexer.ll | 12 ++ src/bin/dhcp4/dhcp4_parser.yy | 7 + src/bin/dhcp4/dhcp4_srv.h | 4 +- src/bin/dhcp4/main.cc | 11 +- src/bin/dhcp6/ctrl_dhcp6_srv.cc | 134 +++++++------- src/bin/dhcp6/ctrl_dhcp6_srv.h | 20 ++- src/bin/dhcp6/dhcp6_lexer.ll | 12 ++ src/bin/dhcp6/dhcp6_parser.yy | 7 + src/bin/dhcp6/dhcp6_srv.h | 2 +- src/bin/dhcp6/main.cc | 15 +- src/bin/dhcp6/tests/rebind_unittest.cc | 1 + src/lib/cc/tests/simple_parser_unittest.cc | 2 + src/lib/dhcp/iface_mgr.cc | 1 - src/lib/dhcpsrv/parsers/dbaccess_parser.cc | 6 + .../database/scripts/cql/dhcpdb_drop.cql | 2 +- src/share/database/scripts/cql/soft_wipe.cql | 2 + src/share/database/scripts/mysql/.gitignore | 16 +- 27 files changed, 282 insertions(+), 199 deletions(-) diff --git a/doc/examples/kea4/cassandra.json b/doc/examples/kea4/cassandra.json index e26c79a352..7ebf42c2cf 100644 --- a/doc/examples/kea4/cassandra.json +++ b/doc/examples/kea4/cassandra.json @@ -35,6 +35,10 @@ // in milliseconds. The default is 5000 [ms]. "connect-timeout": 5000, + // Maximum number of tries for a statement (i.e. select, insert, update, + // delete) before giving up and throwing an exception + "max-statement-tries": 1, + // This parameter sets the timeout for waiting for a response // from a node. Expressed in milliseconds. The default is 12000 [ms]. "request-timeout": 12000, diff --git a/doc/examples/kea6/backends.json b/doc/examples/kea6/backends.json index 7a3c087aac..f9665bfe29 100644 --- a/doc/examples/kea6/backends.json +++ b/doc/examples/kea6/backends.json @@ -72,7 +72,8 @@ // "type": "cql", // "keyspace": "keatest", // "contact-points": "192.0.2.1,192.0.2.2,192.0.2.3", -// "port": 9042 +// "port": 9042, +// "max-statement-tries": 3 // }, // Addresses will be assigned with preferred and valid lifetimes diff --git a/doc/examples/kea6/cassandra.json b/doc/examples/kea6/cassandra.json index 2733105913..0814ed2017 100644 --- a/doc/examples/kea6/cassandra.json +++ b/doc/examples/kea6/cassandra.json @@ -34,6 +34,10 @@ // in milliseconds. The default is 5000 [ms]. "connect-timeout": 5000, + // Maximum number of tries for a statement (i.e. select, insert, update, + // delete) before giving up and throwing an exception + "max-statement-tries": 1, + // This parameter sets the timeout for waiting for a response // from a node. Expressed in milliseconds. The default is 12000 [ms]. "request-timeout": 12000, diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index b17cd80b86..51f6f0c39c 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -574,6 +574,7 @@ immediately upon detecting a loss of connectivity (MySQL and Postgres only). "type": "cql", "keyspace": "keatest", "contact-points": "192.0.2.1, 192.0.2.2, 192.0.2.3", + "max-statement-tries": 1, "port": 9042, "reconnect-wait-time": 2000, "connect-timeout": 5000, diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index f1e8d8b3da..ca5a087a0d 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -1398,7 +1398,7 @@ temporarily override a list of interface names and listen on all interfaces. s46-br90ipv6-addressfalse s46-dmr91ipv6-prefixfalse s46-v4v6bind92record (ipv4-address, ipv6-prefix)false -s46-portparams93record(uint8, psid)false +s46-portparams93record (uint8, psid)false s46-cont-mape94emptyfalse s46-cont-mapt95emptyfalse s46-cont-lw96emptyfalse diff --git a/src/bin/admin/kea-admin.xml b/src/bin/admin/kea-admin.xml index ee17390f52..3bec8e6d2e 100644 --- a/src/bin/admin/kea-admin.xml +++ b/src/bin/admin/kea-admin.xml @@ -73,6 +73,35 @@
+ + -h or --help + + Displays help information. + + + + + db-create + + Creates new empty databases. Useful for first time + installation. + + + + + db-remove + + Removes an existing database. + + + + + create-db-and-users + + Internal use only. + + + lease-init diff --git a/src/bin/agent/Makefile.am b/src/bin/agent/Makefile.am index 6b346fb159..cfb0988543 100644 --- a/src/bin/agent/Makefile.am +++ b/src/bin/agent/Makefile.am @@ -19,7 +19,7 @@ EXTRA_DIST += agent.dox agent_hooks.dox if GENERATE_DOCS kea-ctrl-agent.8: kea-ctrl-agent.xml @XSLTPROC@ --novalid --xinclude --nonet -o $@ \ - http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl \ + http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl \ $(srcdir)/kea-ctrl-agent.xml else @@ -109,6 +109,7 @@ parser: agent_lexer.cc location.hh position.hh stack.hh agent_parser.cc agent_pa # Note C++11 deprecated register still used by flex < 2.6.0 location.hh position.hh stack.hh agent_parser.cc agent_parser.h: agent_parser.yy $(YACC) --defines=agent_parser.h --report=all --report-file=agent_parser.report -o agent_parser.cc agent_parser.yy + $(YACC) --graph --defines=agent_parser.h --report=all --report-file=agent_parser.report -o agent_parser.cc agent_parser.yy agent_lexer.cc: agent_lexer.ll $(LEX) --prefix agent_ -o agent_lexer.cc agent_lexer.ll diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am index 9b11c32e5f..ea2a487f2f 100644 --- a/src/bin/d2/Makefile.am +++ b/src/bin/d2/Makefile.am @@ -136,6 +136,7 @@ parser: d2_lexer.cc location.hh position.hh stack.hh d2_parser.cc d2_parser.h # Note C++11 deprecated register still used by flex < 2.6.0 location.hh position.hh stack.hh d2_parser.cc d2_parser.h: d2_parser.yy $(YACC) --defines=d2_parser.h --report=all --report-file=d2_parser.report -o d2_parser.cc d2_parser.yy + $(YACC) --graph --defines=d2_parser.h --report=all --report-file=d2_parser.report -o d2_parser.cc d2_parser.yy d2_lexer.cc: d2_lexer.ll $(LEX) --prefix d2_parser_ -o d2_lexer.cc d2_lexer.ll diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 080112f4e6..cb7fd643da 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -28,10 +30,10 @@ #include #include -using namespace isc::data; +using namespace isc::config; using namespace isc::dhcp; +using namespace isc::data; using namespace isc::hooks; -using namespace isc::config; using namespace isc::stats; using namespace std; @@ -81,34 +83,6 @@ namespace dhcp { ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL; -void -ControlledDhcpv4Srv::init(const std::string& file_name) { - // Configure the server using JSON file. - ConstElementPtr result = loadConfigFile(file_name); - int rcode; - ConstElementPtr comment = isc::config::parseAnswer(rcode, result); - if (rcode != 0) { - string reason = comment ? comment->stringValue() : - "no details available"; - isc_throw(isc::BadValue, reason); - } - - // We don't need to call openActiveSockets() or startD2() as these - // methods are called in processConfig() which is called by - // processCommand("config-set", ...) - - // Set signal handlers. When the SIGHUP is received by the process - // the server reconfiguration will be triggered. When SIGTERM or - // SIGINT will be received, the server will start shutting down. - signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM)); - // Set the pointer to the handler function. - signal_handler_ = signalHandler; -} - -void ControlledDhcpv4Srv::cleanup() { - // Nothing to do here. No need to disconnect from anything. -} - /// @brief Configure DHCPv4 server using the configuration file specified. /// /// This function is used to both configure the DHCP server on its startup @@ -131,8 +105,8 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) { try { if (file_name.empty()) { // Basic sanity check: file name must not be empty. - isc_throw(isc::BadValue, "JSON configuration file not specified." - " Please use -c command line option."); + isc_throw(isc::BadValue, "JSON configuration file not specified. Please " + "use -c command line option."); } // Read contents of the file and parse it as JSON @@ -186,6 +160,33 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) { return (result); } +void +ControlledDhcpv4Srv::init(const std::string& file_name) { + // Configure the server using JSON file. + ConstElementPtr result = loadConfigFile(file_name); + int rcode; + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); + if (rcode != 0) { + string reason = comment ? comment->stringValue() : + "no details available"; + isc_throw(isc::BadValue, reason); + } + + // We don't need to call openActiveSockets() or startD2() as these + // methods are called in processConfig() which is called by + // processCommand("config-set", ...) + + // Set signal handlers. When the SIGHUP is received by the process + // the server reconfiguration will be triggered. When SIGTERM or + // SIGINT will be received, the server will start shutting down. + signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM)); + // Set the pointer to the handler function. + signal_handler_ = signalHandler; +} + +void ControlledDhcpv4Srv::cleanup() { + // Nothing to do here. No need to disconnect from anything. +} ConstElementPtr ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) { @@ -193,28 +194,26 @@ ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) { ControlledDhcpv4Srv::getInstance()->shutdown(); } else { LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING); - ConstElementPtr answer = isc::config::createAnswer(1, - "Shutdown failure."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down."); return (answer); } ConstElementPtr ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) { - /// @todo delete any stored CalloutHandles referring to the old libraries /// Get list of currently loaded libraries and reload them. HookLibsCollection loaded = HooksManager::getLibraryInfo(); bool status = HooksManager::loadLibraries(loaded); if (!status) { LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL); - ConstElementPtr answer = isc::config::createAnswer(1, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to reload hooks libraries."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Hooks libraries successfully reloaded."); return (answer); } @@ -222,7 +221,6 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) { ConstElementPtr ControlledDhcpv4Srv::commandConfigReloadHandler(const string&, ConstElementPtr /*args*/) { - // Get configuration file name. std::string file = ControlledDhcpv4Srv::getInstance()->getConfigFile(); try { @@ -248,8 +246,7 @@ ControlledDhcpv4Srv::commandConfigGetHandler(const string&, } ConstElementPtr -ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, - ConstElementPtr args) { +ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, ConstElementPtr args) { string filename; if (args) { @@ -268,6 +265,7 @@ ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, if (filename.empty()) { // filename parameter was not specified, so let's use whatever we remember + // from the command-line filename = getConfigFile(); } @@ -301,7 +299,7 @@ ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, ConstElementPtr ControlledDhcpv4Srv::commandConfigSetHandler(const string&, ConstElementPtr args) { - const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error + const int status_code = CONTROL_RESULT_ERROR; ConstElementPtr dhcp4; string message; @@ -353,7 +351,7 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&, // the logging first in case there's a configuration failure. int rcode = 0; isc::config::parseAnswer(rcode, result); - if (rcode == 0) { + if (rcode == CONTROL_RESULT_SUCCESS) { CfgMgr::instance().getStagingCfg()->applyLoggingCfg(); // Use new configuration. @@ -469,17 +467,16 @@ ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) { ElementPtr extended = Element::create(Dhcpv4Srv::getVersion(true)); ElementPtr arguments = Element::createMap(); arguments->set("extended", extended); - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, Dhcpv4Srv::getVersion(false), arguments); return (answer); } ConstElementPtr -ControlledDhcpv4Srv::commandBuildReportHandler(const string&, - ConstElementPtr) { +ControlledDhcpv4Srv::commandBuildReportHandler(const string&, ConstElementPtr) { ConstElementPtr answer = - isc::config::createAnswer(0, isc::detail::getConfigReport()); + isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport()); return (answer); } @@ -520,8 +517,8 @@ ControlledDhcpv4Srv::processCommand(const string& command, ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance(); if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer(1, - "Server object not initialized, so can't process command '" + + ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR, + "Server object not initialized, can't process command '" + command + "', arguments: '" + txt + "'."); return (no_srv); } @@ -564,10 +561,10 @@ ControlledDhcpv4Srv::processCommand(const string& command, return (srv->commandConfigWriteHandler(command, args)); } - return(isc::config::createAnswer(1, "Unrecognized command: " + + return(isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command: " + command)); } catch (const Exception& ex) { - return (isc::config::createAnswer(1, "Error while processing command '" + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '" + command + "': " + ex.what() + ", params: '" + txt + "'")); } @@ -588,7 +585,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Failed to process server identifier: " + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to process server identifier: " + string(ex.what()))); } @@ -605,7 +602,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to process server configuration type: " + string(ex.what()))); } @@ -694,7 +691,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { if (!srv) { err << "Server object not initialized, can't process config."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } answer = configureDhcp4Server(*srv, config); @@ -709,7 +706,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { } } catch (const std::exception& ex) { err << "Failed to process configuration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Re-open lease and host database with new parameters. @@ -721,7 +718,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { cfg_db->createManagers(); } catch (const std::exception& ex) { err << "Unable to open database: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Server will start DDNS communications if its enabled. @@ -730,7 +727,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { } catch (const std::exception& ex) { err << "Error starting DHCP_DDNS client after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Setup DHCPv4-over-DHCPv6 IPC @@ -740,7 +737,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { std::ostringstream err; err << "error starting DHCPv4-over-DHCPv6 IPC " " after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Configuration may change active interfaces. Therefore, we have to reopen @@ -750,8 +747,8 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { // log warnings. Since we allow that this fails for some interfaces there // is no need to rollback configuration if socket fails to open on any // of the interfaces. - CfgMgr::instance().getStagingCfg()->getCfgIface()-> - openSockets(AF_INET, srv->getPort(), getInstance()->useBroadcast()); + CfgMgr::instance().getStagingCfg()->getCfgIface()->openSockets(AF_INET, srv->getPort(), + getInstance()->useBroadcast()); // Install the timers for handling leases reclamation. try { @@ -764,9 +761,13 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { err << "unable to setup timers for periodically running the" " reclamation of the expired leases: " << ex.what() << "."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } + // Finally, we can commit runtime option definitions in libdhcp++. This is + // exception free. + LibDHCP::commitRuntimeOptionDefs(); + // This hook point notifies hooks libraries that the configuration of the // DHCPv4 server has completed. It provides the hook library with the pointer // to the common IO service object, new server configuration in the JSON @@ -794,7 +795,7 @@ isc::data::ConstElementPtr ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) { LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED) - .arg(config->str()); + .arg(config->str()); ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance(); @@ -803,7 +804,7 @@ ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) { if (!srv) { err << "Server object not initialized, can't process config."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } return (configureDhcp4Server(*srv, config, true)); @@ -865,21 +866,20 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) CommandMgr::instance().registerCommand("statistic-get", boost::bind(&StatsMgr::statisticGetHandler, _1, _2)); - CommandMgr::instance().registerCommand("statistic-reset", - boost::bind(&StatsMgr::statisticResetHandler, _1, _2)); - - CommandMgr::instance().registerCommand("statistic-remove", - boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2)); - CommandMgr::instance().registerCommand("statistic-get-all", boost::bind(&StatsMgr::statisticGetAllHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-reset", + boost::bind(&StatsMgr::statisticResetHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-reset-all", boost::bind(&StatsMgr::statisticResetAllHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-remove", + boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-remove-all", boost::bind(&StatsMgr::statisticRemoveAllHandler, _1, _2)); - } void ControlledDhcpv4Srv::shutdown() { @@ -903,14 +903,14 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { // Deregister any registered commands (please keep in alphabetic order) CommandMgr::instance().deregisterCommand("build-report"); CommandMgr::instance().deregisterCommand("config-get"); + CommandMgr::instance().deregisterCommand("config-set"); CommandMgr::instance().deregisterCommand("config-reload"); CommandMgr::instance().deregisterCommand("config-test"); CommandMgr::instance().deregisterCommand("config-write"); - CommandMgr::instance().deregisterCommand("leases-reclaim"); - CommandMgr::instance().deregisterCommand("libreload"); - CommandMgr::instance().deregisterCommand("config-set"); CommandMgr::instance().deregisterCommand("dhcp-disable"); CommandMgr::instance().deregisterCommand("dhcp-enable"); + CommandMgr::instance().deregisterCommand("leases-reclaim"); + CommandMgr::instance().deregisterCommand("libreload"); CommandMgr::instance().deregisterCommand("shutdown"); CommandMgr::instance().deregisterCommand("statistic-get"); CommandMgr::instance().deregisterCommand("statistic-get-all"); @@ -926,8 +926,8 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { ; } - server_ = NULL; // forget this instance. Noone should call any handlers at - // this stage. + server_ = NULL; // forget this instance. There should be no callback anymore + // at this stage anyway. } isc::data::ConstElementPtr @@ -947,14 +947,14 @@ ControlledDhcpv4Srv::readDhcpv4SrvConfigFromMasterDatabase( // Read the contents from database configData = SrvConfigMasterMgrFactory::instance().getConfig4(server_id); if (configData == SrvConfigMasterInfoPtr()) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "No entry found in master database for server id: " + server_id )); } CfgMgr::instance().getStagingCfg()->setMasterServerCfgTimestamp(configData->timestamp_); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Unable to open database: " + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unable to open database: " + std::string(ex.what()))); } @@ -1000,12 +1000,12 @@ ControlledDhcpv4Srv::readDhcpv4SrvConfigFromMasterDatabase( } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Server Configuration error using using database: " + std::string(ex.what()))); } - return (isc::config::createAnswer(0, + return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Server Configuration successfully loaded from database.")); } @@ -1022,11 +1022,11 @@ ControlledDhcpv4Srv::readDhcpv4SrvConfigFromDatabase(isc::data::ElementPtr& db_c // Read the contents from database configData = SrvConfigMgrFactory::instance().getJsonConfig4(); if (configData == SrvConfigInfoPtr()) { - return (isc::config::createAnswer(1, "No entry found in database: ")); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "No entry found in database: ")); } CfgMgr::instance().getStagingCfg()->setServerCfgTimestamp(configData->timestamp_); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Unable to open database: " + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unable to open database: " + std::string(ex.what()))); } @@ -1061,12 +1061,12 @@ ControlledDhcpv4Srv::readDhcpv4SrvConfigFromDatabase(isc::data::ElementPtr& db_c db_config = boost::const_pointer_cast(dhcp4); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Server Configuration error using using database: " + std::string(ex.what()))); } - return (isc::config::createAnswer(0, + return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Server Configuration successfully loaded from database.")); } @@ -1137,7 +1137,7 @@ ControlledDhcpv4Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (!TimerMgr::instance()->isTimerRegistered("Dhcp4DbReconnectTimer")) { TimerMgr::instance()->registerTimer("Dhcp4DbReconnectTimer", boost::bind(&ControlledDhcpv4Srv::dbReconnect, this, - db_reconnect_ctl), + db_reconnect_ctl), db_reconnect_ctl->retryInterval() * 1000, asiolink::IntervalTimer::ONE_SHOT); } diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 2afc95e839..ae158ae158 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -33,7 +33,7 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { ControlledDhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT); /// @brief Destructor. - ~ControlledDhcpv4Srv(); + virtual ~ControlledDhcpv4Srv(); /// @brief Initializes the server. /// @@ -43,12 +43,12 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { /// This method may throw if initialization fails. void init(const std::string& config_file); - /// @brief Loads specific config file + /// @brief Loads specific configuration file /// /// This utility method is called whenever we know a filename of the config /// and need to load it. It calls config-set command once the content of /// the file has been loaded and verified to be a sane JSON configuration. - /// config-set handler will process the config file (load it as current + /// config-set handler will process the config file (apply it as current /// configuration). /// /// @param file_name name of the file to be loaded @@ -121,7 +121,6 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { return (server_); } - private: /// @brief Retrieves the server configuration information from the master database. /// @@ -154,7 +153,7 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { /// @return answer that contains result of the reconfiguration. /// @throw Dhcp4ConfigError if trying to create a parser for NULL config. static isc::data::ConstElementPtr - readDhcpv4SrvConfigFromDatabase(data::ElementPtr& db_config); + readDhcpv4SrvConfigFromDatabase(isc::data::ElementPtr& db_config); /// @brief Callback that will be called from iface_mgr when data /// is received over control socket. @@ -280,7 +279,6 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { commandDhcpEnableHandler(const std::string& command, isc::data::ConstElementPtr args); - /// @Brief handler for processing 'version-get' command /// /// This handler processes version-get command, which returns @@ -396,7 +394,7 @@ class ControlledDhcpv4Srv : public isc::dhcp::Dhcpv4Srv { /// @brief Static pointer to the sole instance of the DHCP server. /// /// This is required for config and command handlers to gain access to - /// the server + /// the server. Some of them need to be static methods. static ControlledDhcpv4Srv* server_; /// @brief IOService object, used for all ASIO operations. diff --git a/src/bin/dhcp4/dhcp4_lexer.ll b/src/bin/dhcp4/dhcp4_lexer.ll index e14e183187..cba865ddca 100644 --- a/src/bin/dhcp4/dhcp4_lexer.ll +++ b/src/bin/dhcp4/dhcp4_lexer.ll @@ -565,6 +565,18 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } +\"max-statement-tries\" { + switch(driver.ctx_) { + case isc::dhcp::Parser4Context::MASTER_DATABASE: + case isc::dhcp::Parser4Context::CONFIG_DATABASE: + case isc::dhcp::Parser4Context::LEASE_DATABASE: + case isc::dhcp::Parser4Context::HOSTS_DATABASE: + return isc::dhcp::Dhcp4Parser::make_MAX_STATEMENT_TRIES(driver.loc_); + default: + return isc::dhcp::Dhcp4Parser::make_STRING("max-statement-tries", driver.loc_); + } +} + \"valid-lifetime\" { switch(driver.ctx_) { case isc::dhcp::Parser4Context::DHCP4: diff --git a/src/bin/dhcp4/dhcp4_parser.yy b/src/bin/dhcp4/dhcp4_parser.yy index 34b1d4aecc..82113eabc5 100644 --- a/src/bin/dhcp4/dhcp4_parser.yy +++ b/src/bin/dhcp4/dhcp4_parser.yy @@ -94,6 +94,7 @@ using namespace std; CONTACT_POINTS "contact-points" KEYSPACE "keyspace" MAX_RECONNECT_TRIES "max-reconnect-tries" + MAX_STATEMENT_TRIES "max-statement-tries" VALID_LIFETIME "valid-lifetime" RENEW_TIMER "renew-timer" @@ -654,6 +655,7 @@ database_map_param: database_type | tcp_keepalive | contact_points | max_reconnect_tries + | max_statement_tries | keyspace | unknown_map_entry ; @@ -769,6 +771,11 @@ max_reconnect_tries: MAX_RECONNECT_TRIES COLON INTEGER { ctx.stack_.back()->set("max-reconnect-tries", n); }; +max_statement_tries: MAX_STATEMENT_TRIES COLON INTEGER { + ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("max-statement-tries", n); +}; + host_reservation_identifiers: HOST_RESERVATION_IDENTIFIERS { ElementPtr l(new ListElement(ctx.loc2pos(@1))); ctx.stack_.back()->set("host-reservation-identifiers", l); diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index c1117316e9..b91604aaf4 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -282,8 +282,8 @@ class Dhcpv4Srv : public Daemon { /// /// @brief Get UDP port on which server should listen. /// - /// Typically, server listens on UDP port number 67. Other ports are used - /// for testing purposes only. + /// Typically, server listens on UDP port 67. Other ports are only + /// used for testing purposes. /// /// @return UDP port on which server should listen. uint16_t getPort() const { diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc index 01325d43b1..265721c3c1 100644 --- a/src/bin/dhcp4/main.cc +++ b/src/bin/dhcp4/main.cc @@ -29,6 +29,9 @@ using namespace std; /// instantiates ControlledDhcpv4Srv class that is responsible for establishing /// connection with msgq (receiving commands and configuration) and also /// creating Dhcpv4 server object as well. +/// +/// For detailed explanation or relations between main(), ControlledDhcpv4Srv, +/// Dhcpv4Srv and other classes, see \ref dhcpv4Session. namespace { @@ -58,7 +61,7 @@ usage() { int main(int argc, char* argv[]) { int ch; - int port_number = DHCP4_SERVER_PORT; // The default. any other values are + int port_number = DHCP4_SERVER_PORT; // The default. Any other values are // useful for testing only. bool verbose_mode = false; // Should server be verbose? bool check_mode = false; // Check syntax @@ -92,7 +95,7 @@ main(int argc, char* argv[]) { config_file = optarg; break; - case 'p': + case 'p': // port number try { port_number = boost::lexical_cast(optarg); } catch (const boost::bad_lexical_cast &) { @@ -117,7 +120,6 @@ main(int argc, char* argv[]) { usage(); } - // Configuration file is required. if (config_file.empty()) { cerr << "Configuration file not specified." << endl; @@ -129,7 +131,6 @@ main(int argc, char* argv[]) { if (check_mode) { try { - // We need to initialize logging, in case any error messages are to be printed. // This is just a test, so we don't care about lockfile. setenv("KEA_LOCKFILE_DIR", "none", 0); @@ -195,7 +196,7 @@ main(int argc, char* argv[]) { // Remember verbose-mode server.setVerbose(verbose_mode); - // Create our PID file. + // Create our PID file server.setProcName(DHCP4_NAME); server.setConfigFile(config_file); server.createPIDFile(); diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index c923cdba8f..94dfedf11c 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -9,17 +9,18 @@ #include #include #include +#include +#include #include +#include +#include +#include #include #include #include #include #include -#include -#include -#include -#include -#include +#include #include #include #include @@ -142,8 +143,7 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) { // Now check is the returned result is successful (rcode=0) or not // (see @ref isc::config::parseAnswer). int rcode; - isc::data::ConstElementPtr comment = - isc::config::parseAnswer(rcode, result); + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); if (rcode != 0) { string reason = comment ? comment->stringValue() : "no details available"; @@ -163,7 +163,6 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) { return (result); } - void ControlledDhcpv6Srv::init(const std::string& file_name) { // Configure the server using JSON file. @@ -192,17 +191,16 @@ void ControlledDhcpv6Srv::cleanup() { // Nothing to do here. No need to disconnect from anything. } - ConstElementPtr ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr) { - if (ControlledDhcpv6Srv::server_) { - ControlledDhcpv6Srv::server_->shutdown(); + if (ControlledDhcpv6Srv::getInstance()) { + ControlledDhcpv6Srv::getInstance()->shutdown(); } else { LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING); - ConstElementPtr answer = isc::config::createAnswer(1, "Shutdown failure."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down."); + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down."); return (answer); } @@ -214,11 +212,11 @@ ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) { bool status = HooksManager::loadLibraries(loaded); if (!status) { LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL); - ConstElementPtr answer = isc::config::createAnswer(1, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to reload hooks libraries."); return (answer); } - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Hooks libraries successfully reloaded."); return (answer); } @@ -472,7 +470,7 @@ ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) { ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true)); ElementPtr arguments = Element::createMap(); arguments->set("extended", extended); - ConstElementPtr answer = isc::config::createAnswer(0, + ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, Dhcpv6Srv::getVersion(false), arguments); return (answer); @@ -481,14 +479,14 @@ ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) { ConstElementPtr ControlledDhcpv6Srv::commandBuildReportHandler(const string&, ConstElementPtr) { ConstElementPtr answer = - isc::config::createAnswer(0, isc::detail::getConfigReport()); + isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport()); return (answer); } ConstElementPtr ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&, ConstElementPtr args) { - int status_code = 1; + int status_code = CONTROL_RESULT_ERROR; string message; // args must be { "remove": } @@ -511,9 +509,9 @@ ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&, return (answer); } -isc::data::ConstElementPtr -ControlledDhcpv6Srv::processCommand(const std::string& command, - isc::data::ConstElementPtr args) { +ConstElementPtr +ControlledDhcpv6Srv::processCommand(const string& command, + ConstElementPtr args) { string txt = args ? args->str() : "(none)"; LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED) @@ -522,7 +520,7 @@ ControlledDhcpv6Srv::processCommand(const std::string& command, ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer(1, + ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Server object not initialized, can't process command '" + command + "', arguments: '" + txt + "'."); return (no_srv); @@ -566,10 +564,10 @@ ControlledDhcpv6Srv::processCommand(const std::string& command, return (srv->commandConfigWriteHandler(command, args)); } - return (isc::config::createAnswer(1, "Unrecognized command: " + - command)); + return(isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command: " + + command)); } catch (const Exception& ex) { - return (isc::config::createAnswer(1, "Error while processing command '" + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '" + command + "': " + ex.what() + ", params: '" + txt + "'")); } @@ -590,7 +588,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Failed to process server identifier: " + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to process server identifier: " + string(ex.what()))); } @@ -607,7 +605,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Failed to process server configuration type: " + string(ex.what()))); } @@ -691,11 +689,12 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); + // Single stream instance used in all error clauses + std::ostringstream err; + if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer( - CONTROL_RESULT_ERROR, - "Server object not initialized, can't process config."); - return (no_srv); + err << "Server object not initialized, can't process config."; + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } answer = configureDhcp6Server(*srv, config); @@ -709,8 +708,8 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { return (answer); } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Failed to process configuration: " + - string(ex.what()))); + err << "Failed to process configuration: " << ex.what(); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Re-open lease and host database with new parameters. @@ -721,8 +720,8 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { cfg_db->setAppendedParameters("universe=6"); cfg_db->createManagers(); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Unable to open database: " - + std::string(ex.what()))); + err << "Unable to open database: " << ex.what(); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Regenerate server identifier if needed. @@ -740,17 +739,16 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { } catch (const std::exception& ex) { std::ostringstream err; err << "unable to configure server identifier: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Server will start DDNS communications if its enabled. try { srv->startD2(); } catch (const std::exception& ex) { - std::ostringstream err; - err << "error starting DHCP_DDNS client " - " after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + err << "Error starting DHCP_DDNS client after server reconfiguration: " + << ex.what(); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Setup DHCPv4-over-DHCPv6 IPC @@ -760,7 +758,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { std::ostringstream err; err << "error starting DHCPv4-over-DHCPv6 IPC " " after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Configuration may change active interfaces. Therefore, we have to reopen @@ -780,11 +778,10 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { server_); } catch (const std::exception& ex) { - std::ostringstream err; err << "unable to setup timers for periodically running the" " reclamation of the expired leases: " << ex.what() << "."; - return (isc::config::createAnswer(1, err.str())); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } // Finally, we can commit runtime option definitions in libdhcp++. This is @@ -822,10 +819,12 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) { ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); + // Single stream instance used in all error clauses + std::ostringstream err; + if (!srv) { - ConstElementPtr no_srv = isc::config::createAnswer(1, - "Server object not initialized, can't process config."); - return (no_srv); + err << "Server object not initialized, can't process config."; + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str())); } return (configureDhcp6Server(*srv, config, true)); @@ -833,11 +832,11 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) { ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) : Dhcpv6Srv(port), io_service_(), timer_mgr_(TimerMgr::instance()) { - if (server_) { + if (getInstance()) { isc_throw(InvalidOperation, "There is another Dhcpv6Srv instance already."); } - server_ = this; // remember this instance for use in callback + server_ = this; // remember this instance for later use in handlers // TimerMgr uses IO service to run asynchronous timers. TimerMgr::instance()->setIOService(getIOService()); @@ -856,26 +855,26 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) CommandMgr::instance().registerCommand("config-reload", boost::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, _1, _2)); + CommandMgr::instance().registerCommand("config-set", + boost::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, _1, _2)); + CommandMgr::instance().registerCommand("config-test", boost::bind(&ControlledDhcpv6Srv::commandConfigTestHandler, this, _1, _2)); CommandMgr::instance().registerCommand("config-write", boost::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("dhcp-disable", - boost::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("dhcp-enable", boost::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("leases-reclaim", - boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2)); + CommandMgr::instance().registerCommand("dhcp-disable", + boost::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, _1, _2)); CommandMgr::instance().registerCommand("libreload", boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("config-set", - boost::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, _1, _2)); + CommandMgr::instance().registerCommand("leases-reclaim", + boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2)); CommandMgr::instance().registerCommand("shutdown", boost::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, _1, _2)); @@ -968,14 +967,14 @@ ControlledDhcpv6Srv::readDhcpv6SrvConfigFromMasterDatabase( // Read the contents from database configData = SrvConfigMasterMgrFactory::instance().getConfig6(server_id); if (configData == SrvConfigMasterInfoPtr()) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "No entry found in master database for server id: " + server_id )); } CfgMgr::instance().getStagingCfg()->setMasterServerCfgTimestamp(configData->timestamp_); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Unable to open database: " + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unable to open database: " + std::string(ex.what()))); } @@ -1021,12 +1020,12 @@ ControlledDhcpv6Srv::readDhcpv6SrvConfigFromMasterDatabase( } } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Server Configuration error using using database: " + std::string(ex.what()))); } - return (isc::config::createAnswer(0, + return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Server Configuration successfully loaded from database.")); } @@ -1043,11 +1042,11 @@ ControlledDhcpv6Srv::readDhcpv6SrvConfigFromDatabase(isc::data::ElementPtr& db_c // Read the contents from database configData = SrvConfigMgrFactory::instance().getJsonConfig6(); if (configData == SrvConfigInfoPtr()) { - return (isc::config::createAnswer(1, "No entry found in database: ")); + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "No entry found in database: ")); } CfgMgr::instance().getStagingCfg()->setServerCfgTimestamp(configData->timestamp_); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, "Unable to open database: " + + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unable to open database: " + std::string(ex.what()))); } @@ -1082,20 +1081,20 @@ ControlledDhcpv6Srv::readDhcpv6SrvConfigFromDatabase(isc::data::ElementPtr& db_c db_config = boost::const_pointer_cast(dhcp6); } catch (const std::exception& ex) { - return (isc::config::createAnswer(1, + return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Server Configuration error using using database: " + std::string(ex.what()))); } - return (isc::config::createAnswer(0, + return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Server Configuration successfully loaded from database.")); } void ControlledDhcpv6Srv::sessionReader(void) { // Process one asio event. If there are more events, iface_mgr will call // this callback more than once. - if (server_) { - server_->io_service_.run_one(); + if (getInstance()) { + getInstance()->io_service_.run_one(); } } @@ -1134,12 +1133,13 @@ ControlledDhcpv6Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (reopened) { // Cancel the timer. if (TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) { - TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer"); } + TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer"); + } // Set network state to service enabled network_state_->enableService(); - // Toss the reconnct control, we're done with it + // Toss the reconnect control, we're done with it db_reconnect_ctl.reset(); } else { if (!db_reconnect_ctl->checkRetries()) { diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index 6e14eb51d6..5df28b2065 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -65,7 +65,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { /// @brief Initiates shutdown procedure for the whole DHCPv6 server. void shutdown(); - /// @brief command processor + /// @brief Command processor /// /// This method is uniform for all config backends. It processes received /// command (as a string + JSON arguments). Internally, it's just a @@ -75,9 +75,9 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { /// Currently supported commands are: /// - config-reload /// - config-test - /// - leases-reclaim - /// - libreload /// - shutdown + /// - libreload + /// - leases-reclaim /// ... /// /// @note It never throws. @@ -89,7 +89,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { static isc::data::ConstElementPtr processCommand(const std::string& command, isc::data::ConstElementPtr args); - /// @brief configuration processor + /// @brief Configuration processor /// /// This is a method for handling incoming configuration updates. /// This method should be called by all configuration backends when the @@ -114,7 +114,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr new_config); - /// @brief returns pointer to the sole instance of Dhcpv6Srv + /// @brief Returns pointer to the sole instance of Dhcpv6Srv /// /// @return server instance (may return NULL, if called before server is spawned) static ControlledDhcpv6Srv* getInstance() { @@ -163,7 +163,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { /// (that was sent from some yet unspecified sender). static void sessionReader(void); - /// @brief handler for processing 'shutdown' command + /// @brief Handler for processing 'shutdown' command /// /// This handler processes shutdown command, which initializes shutdown /// procedure. @@ -175,7 +175,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { commandShutdownHandler(const std::string& command, isc::data::ConstElementPtr args); - /// @brief handler for processing 'libreload' command + /// @brief Handler for processing 'libreload' command /// /// This handler processes libreload command, which unloads all hook /// libraries and reloads them. @@ -188,7 +188,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { commandLibReloadHandler(const std::string& command, isc::data::ConstElementPtr args); - /// @brief handler for processing 'config-reload' command + /// @brief Handler for processing 'config-reload' command /// /// This handler processes config-reload command, which processes /// configuration specified in args parameter. @@ -341,7 +341,6 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { const bool remove_lease, const uint16_t max_unwarned_cycles); - /// @brief Deletes reclaimed leases and reschedules the timer. /// /// This is a wrapper method for @c AllocEngine::deleteExpiredReclaimed6. @@ -366,6 +365,7 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { /// /// If the maximum number of retries has been exhausted an error is logged /// and the server shuts down. + /// /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the /// configured reconnect parameters /// @@ -387,6 +387,8 @@ class ControlledDhcpv6Srv : public isc::dhcp::Dhcpv6Srv { /// /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the /// configured reconnect parameters + /// + /// @return false if reconnect is not configured, true otherwise bool dbLostCallback(ReconnectCtlPtr db_reconnect_ctl); /// @brief Static pointer to the sole instance of the DHCP server. diff --git a/src/bin/dhcp6/dhcp6_lexer.ll b/src/bin/dhcp6/dhcp6_lexer.ll index 16ea120e72..82a4cfc567 100644 --- a/src/bin/dhcp6/dhcp6_lexer.ll +++ b/src/bin/dhcp6/dhcp6_lexer.ll @@ -739,6 +739,18 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} } } +\"max-statement-tries\" { + switch(driver.ctx_) { + case isc::dhcp::Parser6Context::MASTER_DATABASE: + case isc::dhcp::Parser6Context::CONFIG_DATABASE: + case isc::dhcp::Parser6Context::LEASE_DATABASE: + case isc::dhcp::Parser6Context::HOSTS_DATABASE: + return isc::dhcp::Dhcp6Parser::make_MAX_STATEMENT_TRIES(driver.loc_); + default: + return isc::dhcp::Dhcp6Parser::make_STRING("max-statement-tries", driver.loc_); + } +} + \"preferred-lifetime\" { switch(driver.ctx_) { case isc::dhcp::Parser6Context::DHCP6: diff --git a/src/bin/dhcp6/dhcp6_parser.yy b/src/bin/dhcp6/dhcp6_parser.yy index 3a9f60a178..5698eb6175 100644 --- a/src/bin/dhcp6/dhcp6_parser.yy +++ b/src/bin/dhcp6/dhcp6_parser.yy @@ -82,6 +82,7 @@ using namespace std; CONTACT_POINTS "contact-points" KEYSPACE "keyspace" MAX_RECONNECT_TRIES "max-reconnect-tries" + MAX_STATEMENT_TRIES "max-statement-tries" PREFERRED_LIFETIME "preferred-lifetime" VALID_LIFETIME "valid-lifetime" @@ -623,6 +624,7 @@ database_map_param: database_type | tcp_keepalive | contact_points | max_reconnect_tries + | max_statement_tries | keyspace | unknown_map_entry ; @@ -738,6 +740,11 @@ max_reconnect_tries: MAX_RECONNECT_TRIES COLON INTEGER { ctx.stack_.back()->set("max-reconnect-tries", n); }; +max_statement_tries: MAX_STATEMENT_TRIES COLON INTEGER { + ElementPtr n(new IntElement($3, ctx.loc2pos(@3))); + ctx.stack_.back()->set("max-statement-tries", n); +}; + mac_sources: MAC_SOURCES { ElementPtr l(new ListElement(ctx.loc2pos(@1))); ctx.stack_.back()->set("mac-sources", l); diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index df259594a3..933f2c54d5 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -51,7 +51,7 @@ class DHCPv6DiscardMessageError : public Exception { /// @brief DHCPv6 server service. /// -/// This class represents DHCPv6 server. It contains all +/// This singleton class represents DHCPv6 server. It contains all /// top-level methods and routines necessary for server operation. /// In particular, it instantiates IfaceMgr, loads or generates DUID /// that is going to be used as server-identifier, receives incoming diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc index 0538e58b4c..85ad7ee056 100644 --- a/src/bin/dhcp6/main.cc +++ b/src/bin/dhcp6/main.cc @@ -171,11 +171,8 @@ main(int argc, char* argv[]) { cerr << "Error encountered: " << answer->stringValue() << endl; return (EXIT_FAILURE); } - - - return (EXIT_SUCCESS); } catch (const std::exception& ex) { - cerr << "Syntax check failed with " << ex.what() << endl; + cerr << "Syntax check failed with: " << ex.what() << endl; } return (EXIT_FAILURE); } @@ -189,7 +186,6 @@ main(int argc, char* argv[]) { // Initialize logging. If verbose, we'll use maximum verbosity. Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode); - LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO) .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no"); @@ -207,10 +203,8 @@ main(int argc, char* argv[]) { server.createPIDFile(); try { - // Initialize the server, e.g. establish control session - // Read a configuration file + // Initialize the server. server.init(config_file); - } catch (const std::exception& ex) { try { @@ -220,8 +214,8 @@ main(int argc, char* argv[]) { LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what()); } catch (...) { // The exception thrown during the initialization could - // originate from logger subsystem. Therefore LOG_ERROR() may - // fail as well. + // originate from logger subsystem. Therefore LOG_ERROR() + // may fail as well. cerr << "Failed to initialize server: " << ex.what() << endl; } @@ -252,7 +246,6 @@ main(int argc, char* argv[]) { } ret = EXIT_FAILURE; } catch (const std::exception& ex) { - // First, we print the error on stderr (that should always work) cerr << DHCP6_NAME << "Fatal error during start up: " << ex.what() << endl; diff --git a/src/bin/dhcp6/tests/rebind_unittest.cc b/src/bin/dhcp6/tests/rebind_unittest.cc index f2c146b7f4..d446e4d824 100644 --- a/src/bin/dhcp6/tests/rebind_unittest.cc +++ b/src/bin/dhcp6/tests/rebind_unittest.cc @@ -473,6 +473,7 @@ TEST_F(RebindTest, directClientLostLease) { // The lease has been acquired. Now, let's explicitly remove it from the // lease database. LeaseMgrFactory::instance().deleteLease(lease_client.addr_); + // Send Rebind. ASSERT_NO_THROW(client.doRebind()); diff --git a/src/lib/cc/tests/simple_parser_unittest.cc b/src/lib/cc/tests/simple_parser_unittest.cc index 9febfe868e..ae2fc17633 100644 --- a/src/lib/cc/tests/simple_parser_unittest.cc +++ b/src/lib/cc/tests/simple_parser_unittest.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index edeb38b0db..d7de0a8c7b 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -885,7 +885,6 @@ IfaceMgr::send(const Pkt4Ptr& pkt) { return (packet_filter_->send(*iface, getSocket(*pkt).sockfd_, pkt)); } - Pkt4Ptr IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc index 5e90cc7e8d..f57aa5914d 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc @@ -58,6 +58,7 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, int64_t timeout = 0; int64_t port = 0; int64_t max_reconnect_tries = 0; + int64_t max_statement_tries = 0; int64_t reconnect_wait_time = 0; int64_t request_timeout = 0; int64_t tcp_keepalive = 0; @@ -106,6 +107,11 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, values_copy[param.first] = boost::lexical_cast(port); + } else if (param.first == "max-statement-tries") { + max_statement_tries = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast(max_statement_tries); + } else { // all remaining string parameters // type diff --git a/src/share/database/scripts/cql/dhcpdb_drop.cql b/src/share/database/scripts/cql/dhcpdb_drop.cql index b29538fef8..765db53455 100644 --- a/src/share/database/scripts/cql/dhcpdb_drop.cql +++ b/src/share/database/scripts/cql/dhcpdb_drop.cql @@ -1,4 +1,4 @@ --- Copyright (C) 2015-2017 Deutsche Telekom AG. +-- Copyright (C) 2015-2018 Deutsche Telekom AG. -- Author: Razvan Becheriu diff --git a/src/share/database/scripts/cql/soft_wipe.cql b/src/share/database/scripts/cql/soft_wipe.cql index 5c9580843c..5a722b9982 100644 --- a/src/share/database/scripts/cql/soft_wipe.cql +++ b/src/share/database/scripts/cql/soft_wipe.cql @@ -24,3 +24,5 @@ TRUNCATE TABLE lease_hwaddr_source; TRUNCATE TABLE lease_state; TRUNCATE TABLE schema_version; TRUNCATE TABLE host_reservations; +TRUNCATE TABLE server_configuration4; +TRUNCATE TABLE server_configuration6; diff --git a/src/share/database/scripts/mysql/.gitignore b/src/share/database/scripts/mysql/.gitignore index 2c634de768..43521f67ca 100644 --- a/src/share/database/scripts/mysql/.gitignore +++ b/src/share/database/scripts/mysql/.gitignore @@ -1,10 +1,10 @@ -/upgrade_1.0_to_2.0.sh -/upgrade_2.0_to_3.0.sh -/upgrade_3.0_to_4.0.sh -/upgrade_4.0_to_4.1.sh -/upgrade_4.1_to_5.0.sh -/upgrade_5.0_to_5.1.sh -/upgrade_5.1_to_5.2.sh -/upgrade_5.2_to_6.0.sh +upgrade_1.0_to_2.0.sh +upgrade_2.0_to_3.0.sh +upgrade_3.0_to_4.0.sh +upgrade_4.0_to_4.1.sh +upgrade_4.1_to_5.0.sh +upgrade_5.0_to_5.1.sh +upgrade_5.1_to_5.2.sh +upgrade_5.2_to_6.0.sh config_upgrade_0.0_to_1.0.sh master_upgrade_0.0_to_1.0.sh From 153a9eb90a281d0fb0b300247f9ba946232dfee6 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 25 May 2018 14:50:04 +0300 Subject: [PATCH 05/29] minor changes --- src/bin/dhcp4/json_config_parser.cc | 86 ++++++++++++++++------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 7d67cea9ef..2282bd1ef3 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -6,15 +6,21 @@ #include +#include +#include #include #include #include +#include #include +#include #include +#include #include #include -#include #include +#include +#include #include #include #include @@ -26,26 +32,29 @@ #include #include #include -#include #include -#include +#include #include #include +#include #include #include -#include +#include +#include -#include #include +#include +#include #include #include -#include + +#include using namespace std; using namespace isc; -using namespace isc::dhcp; using namespace isc::data; +using namespace isc::dhcp; using namespace isc::asiolink; using namespace isc::hooks; @@ -61,6 +70,7 @@ namespace { /// See @ref parse method for a list of supported parameters. class Dhcp4ConfigParser : public isc::data::SimpleParser { public: + /// @brief Sets global parameters in staging configuration /// /// @param global global configuration scope @@ -75,26 +85,26 @@ class Dhcp4ConfigParser : public isc::data::SimpleParser { /// /// @throw DhcpConfigError if parameters are missing or /// or having incorrect values. - void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) { + void parse(const SrvConfigPtr& srv_config, const ConstElementPtr& global) { // Set whether v4 server is supposed to echo back client-id // (yes = RFC6842 compatible, no = backward compatibility) bool echo_client_id = getBoolean(global, "echo-client-id"); - cfg->setEchoClientId(echo_client_id); + srv_config->setEchoClientId(echo_client_id); // Set the probation period for decline handling. uint32_t probation_period = getUint32(global, "decline-probation-period"); - cfg->setDeclinePeriod(probation_period); + srv_config->setDeclinePeriod(probation_period); // Set the DHCPv4-over-DHCPv6 interserver port. uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port"); - cfg->setDhcp4o6Port(dhcp4o6_port); + srv_config->setDhcp4o6Port(dhcp4o6_port); // Set the global user context. ConstElementPtr user_context = global->get("user-context"); if (user_context) { - cfg->setContext(user_context); + srv_config->setContext(user_context); } } @@ -308,29 +318,32 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, // have to be restored to global storages. bool rollback = false; // config_pair holds the details of the current parser when iterating over - // the parsers. It is declared outside the loops so in case of an error, - // the name of the failing parser can be retrieved in the "catch" clause. + // the parsers. It is declared outside the loop so in case of error, the + // name of the failing parser can be retrieved within the "catch" clause. ConfigPair config_pair; try { - SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); + SrvConfigPtr srv_config = CfgMgr::instance().getStagingCfg(); // This is a way to convert ConstElementPtr to ElementPtr. // We need a config that can be edited, because we will insert // default values and will insert derived values as well. ElementPtr mutable_cfg = boost::const_pointer_cast(config_set); - // Set all default values if not specified by the user. SimpleParser4::setAllDefaults(mutable_cfg); // And now derive (inherit) global parameters to subnets, if not specified. SimpleParser4::deriveParameters(mutable_cfg); + // Make parsers grouping. + const std::map& values_map = + mutable_cfg->mapValue(); + // We need definitions first ConstElementPtr option_defs = mutable_cfg->get("option-def"); if (option_defs) { OptionDefListParser parser; - CfgOptionDefPtr cfg_option_def = srv_cfg->getCfgOptionDef(); + CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef(); parser.parse(cfg_option_def, option_defs); } @@ -338,15 +351,13 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, // early. Dhcp4ConfigParser global_parser; - // Make parsers grouping. - const std::map& values_map = - mutable_cfg->mapValue(); BOOST_FOREACH(config_pair, values_map) { // In principle we could have the following code structured as a series // of long if else if clauses. That would give a marginal performance // boost, but would make the code less readable. We had serious issues // with the parser code debugability, so I decided to keep it as a // series of independent ifs. + if (config_pair.first == "option-def") { // This is converted to SimpleParser and is handled already above. continue; @@ -354,14 +365,14 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, if (config_pair.first == "option-data") { OptionDataListParser parser(AF_INET); - CfgOptionPtr cfg_option = srv_cfg->getCfgOption(); + CfgOptionPtr cfg_option = srv_config->getCfgOption(); parser.parse(cfg_option, config_pair.second); continue; } if (config_pair.first == "control-socket") { ControlSocketParser parser; - parser.parse(*srv_cfg, config_pair.second); + parser.parse(*srv_config, config_pair.second); continue; } @@ -379,7 +390,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, ifaces_cfg->set("re-detect", Element::create(false)); } IfacesConfigParser parser(AF_INET); - CfgIfacePtr cfg_iface = srv_cfg->getCfgIface(); + CfgIfacePtr cfg_iface = srv_config->getCfgIface(); parser.parse(cfg_iface, ifaces_cfg); continue; } @@ -392,7 +403,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, if (config_pair.first == "hooks-libraries") { HooksLibrariesParser hooks_parser; - HooksConfig& libraries = srv_cfg->getHooksConfig(); + HooksConfig& libraries = srv_config->getHooksConfig(); hooks_parser.parse(libraries, config_pair.second); libraries.verifyLibraries(config_pair.second->getPosition()); continue; @@ -404,7 +415,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, D2ClientConfigParser::setAllDefaults(config_pair.second); D2ClientConfigParser parser; D2ClientConfigPtr cfg = parser.parse(config_pair.second); - srv_cfg->setD2ClientConfig(cfg); + srv_config->setD2ClientConfig(cfg); continue; } @@ -412,27 +423,27 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, ClientClassDefListParser parser; ClientClassDictionaryPtr dictionary = parser.parse(config_pair.second, AF_INET); - srv_cfg->setClientClassDictionary(dictionary); + srv_config->setClientClassDictionary(dictionary); continue; } // Please move at the end when migration will be finished. if (config_pair.first == "lease-database") { DbAccessParser parser(DBType::LEASE_DB); - CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; } if (config_pair.first == "hosts-database") { DbAccessParser parser(DBType::HOSTS_DB); - CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; } if (config_pair.first == "hosts-databases") { - CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); DbAccessParser parser(DBType::HOSTS_DB); auto list = config_pair.second->listValue(); for (auto it : list) { @@ -443,23 +454,22 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, if (config_pair.first == "master-database") { DbAccessParser parser(DBType::MASTER_DB); - CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; } if (config_pair.first == "config-database") { DbAccessParser parser(DBType::CONFIG_DB); - CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); + CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; } if (config_pair.first == "subnet4") { - SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); Subnets4ListConfigParser subnets_parser; // parse() returns number of subnets parsed. We may log it one day. - subnets_parser.parse(srv_cfg, config_pair.second); + subnets_parser.parse(srv_config, config_pair.second); continue; } @@ -470,12 +480,12 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, /// add subnets from the CfgSharedNetworks4 into CfgSubnets4 /// as well. SharedNetworks4ListParser parser; - CfgSharedNetworks4Ptr cfg = srv_cfg->getCfgSharedNetworks4(); + CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4(); parser.parse(cfg, config_pair.second); // We also need to put the subnets it contains into normal // subnets list. - global_parser.copySubnets4(srv_cfg->getCfgSubnets4(), cfg); + global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg); continue; } @@ -506,12 +516,12 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, } // Apply global options in the staging config. - global_parser.parse(srv_cfg, mutable_cfg); + global_parser.parse(srv_config, mutable_cfg); // This method conducts final sanity checks and tweaks. In particular, // it checks that there is no conflict between plain subnets and those // defined as part of shared networks. - global_parser.sanityChecks(srv_cfg, mutable_cfg); + global_parser.sanityChecks(srv_config, mutable_cfg); } catch (const isc::Exception& ex) { LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL) @@ -568,12 +578,14 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, catch (const isc::Exception& ex) { LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what()); answer = isc::config::createAnswer(2, ex.what()); + // An error occurred, so make sure to restore the original data. rollback = true; } catch (...) { // For things like bad_cast in boost::lexical_cast LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION); answer = isc::config::createAnswer(2, "undefined configuration" " parsing error"); + // An error occurred, so make sure to restore the original data. rollback = true; } } From 1c62e8c563ec3da6143e906c84a5186010437708 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 25 May 2018 14:51:36 +0300 Subject: [PATCH 06/29] minor changes --- src/bin/dhcp6/json_config_parser.cc | 37 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 0939ae7011..0068396c95 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -9,12 +9,13 @@ #include #include #include -#include -#include -#include #include #include +#include +#include #include +#include +#include #include #include #include @@ -319,8 +320,6 @@ class Dhcp6ConfigParser : public isc::data::SimpleParser { } } - - }; } // namespace @@ -372,7 +371,6 @@ void configureCommandChannel6() { isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, bool check_only) { - if (!config_set) { ConstElementPtr answer = isc::config::createAnswer(1, string("Can't parse NULL config")); @@ -401,14 +399,9 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, // Print the list of known backends. HostDataSourceFactory::printRegistered(); - // This is a way to convert ConstElementPtr to ElementPtr. - // We need a config that can be edited, because we will insert - // default values and will insert derived values as well. - ElementPtr mutable_cfg = boost::const_pointer_cast(config_set); - - // answer will hold the result. + // Answer will hold the result. ConstElementPtr answer; - // rollback informs whether error occurred and original data + // Rollback informs whether error occurred and original data // have to be restored to global storages. bool rollback = false; // config_pair holds the details of the current parser when iterating over @@ -419,6 +412,10 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, SrvConfigPtr srv_config = CfgMgr::instance().getStagingCfg(); + // This is a way to convert ConstElementPtr to ElementPtr. + // We need a config that can be edited, because we will insert + // default values and will insert derived values as well. + ElementPtr mutable_cfg = boost::const_pointer_cast(config_set); // Set all default values if not specified by the user. SimpleParser6::setAllDefaults(mutable_cfg); @@ -513,6 +510,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, continue; } + // Legacy DhcpConfigParser stuff below if (config_pair.first == "dhcp-ddns") { // Apply defaults D2ClientConfigParser::setAllDefaults(config_pair.second); @@ -522,7 +520,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, continue; } - if (config_pair.first =="client-classes") { + if (config_pair.first == "client-classes") { ClientClassDefListParser parser; ClientClassDictionaryPtr dictionary = parser.parse(config_pair.second, AF_INET6); @@ -582,7 +580,6 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, /// CfgSharedNetworks4 object. One additional step is then to /// add subnets from the CfgSharedNetworks6 into CfgSubnets6 /// as well. - SharedNetworks6ListParser parser; CfgSharedNetworks6Ptr cfg = srv_config->getCfgSharedNetworks6(); parser.parse(cfg, config_pair.second); @@ -632,14 +629,16 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL) .arg(config_pair.first).arg(ex.what()); answer = isc::config::createAnswer(1, ex.what()); + // An error occurred, so make sure that we restore original data. rollback = true; } catch (...) { - // for things like bad_cast in boost::lexical_cast + // For things like bad_cast in boost::lexical_cast LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(config_pair.first); answer = isc::config::createAnswer(1, "undefined configuration" " processing error"); + // An error occurred, so make sure that we restore original data. rollback = true; } @@ -666,12 +665,12 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, // No need to commit interface names as this is handled by the // CfgMgr::commit() function. - // Apply staged D2ClientConfig, used to be done by parser commit + // Apply the staged D2ClientConfig, used to be done by parser commit D2ClientConfigPtr cfg; cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig(); CfgMgr::instance().setD2ClientConfig(cfg); - // This occurs last as if it succeeds, there is no easy way to + // This occurs last as if it succeeds, there is no easy way // revert it. As a result, the failure to commit a subsequent // change causes problems when trying to roll back. const HooksConfig& libraries = @@ -684,7 +683,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, // An error occurred, so make sure to restore the original data. rollback = true; } catch (...) { - // for things like bad_cast in boost::lexical_cast + // For things like bad_cast in boost::lexical_cast LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION); answer = isc::config::createAnswer(2, "undefined configuration" " parsing error"); From 71ff1f1e4669c060f73578fc5ee0618bba3e5ace Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 25 May 2018 15:05:54 +0300 Subject: [PATCH 07/29] minor changes --- src/lib/dhcpsrv/alloc_engine.cc | 1 - src/lib/dhcpsrv/cql_connection.cc | 30 +++++++++++++++++++ src/lib/dhcpsrv/cql_connection.h | 4 +++ .../dhcpsrv/tests/cql_lease_mgr_unittest.cc | 4 ++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 2ac0524bc1..429ee942de 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -2603,7 +2603,6 @@ void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease, // expired-reclaimed state or simply remove it. if (remove_lease) { lease_mgr.deleteLease(lease->addr_); - } else if (!lease_update_fun.empty()) { // Clear FQDN information as we have already sent the // name change request to remove the DNS record. diff --git a/src/lib/dhcpsrv/cql_connection.cc b/src/lib/dhcpsrv/cql_connection.cc index 58352fd3d4..995daf26cc 100644 --- a/src/lib/dhcpsrv/cql_connection.cc +++ b/src/lib/dhcpsrv/cql_connection.cc @@ -165,6 +165,15 @@ CqlConnection::openDatabase() { // No tcp-nodelay. Fine, we'll use the default false. } + const char* max_statement_tries = NULL; + std::string smax_statement_tries; + try { + smax_statement_tries = getParameter("max-statement-tries"); + max_statement_tries = smax_statement_tries.c_str(); + } catch (...) { + // No max statement tries. Fine, we'll use the default 1. + } + cluster_ = cass_cluster_new(); cass_cluster_set_contact_points(cluster_, contact_points); @@ -284,6 +293,27 @@ CqlConnection::openDatabase() { session_ = cass_session_new(); + if (max_statement_tries) { + int32_t max_statement_tries_number; + try { + max_statement_tries_number = + boost::lexical_cast(max_statement_tries); + if (max_statement_tries_number < 0) { + isc_throw(DbOperationError, + "CqlConnection::openDatabase(): invalid reconnect " + "wait time, expected positive number, instead got " + << max_statement_tries); + } + } catch (const boost::bad_lexical_cast& ex) { + isc_throw(DbOperationError, + "CqlConnection::openDatabase(): " + "invalid reconnect wait time, expected " + "castable to int, instead got \"" + << max_statement_tries << "\", " << ex.what()); + } + max_statement_tries_ = max_statement_tries_number; + } + CassFuture* connect_future = cass_session_connect_keyspace(session_, cluster_, keyspace); cass_future_wait(connect_future); diff --git a/src/lib/dhcpsrv/cql_connection.h b/src/lib/dhcpsrv/cql_connection.h index 810a802a2f..553f2fd6e9 100644 --- a/src/lib/dhcpsrv/cql_connection.h +++ b/src/lib/dhcpsrv/cql_connection.h @@ -149,6 +149,7 @@ class CqlConnection : public DatabaseConnection { /// - request-timeout 12000 /// - tcp-keepalive no /// - tcp-nodelay no + /// - max-statement-tries 3 /// /// @throw DbOpenError error opening the database void openDatabase(); @@ -199,6 +200,9 @@ class CqlConnection : public DatabaseConnection { /// @brief Keyspace meta information, used for UDTs const CassKeyspaceMeta* keyspace_meta_; + /// @brief Maximum tries for any executeMutation(). + uint32_t max_statement_tries_; + /// @brief CQL consistency enabled bool force_consistency_; }; diff --git a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc index 21603e73d0..0ad41fb592 100644 --- a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc @@ -315,7 +315,9 @@ class CqlLeaseMgrTest : public GenericLeaseMgrTest { // This the returned leases should exclude reclaimed ones. So the number // of returned leases should be roughly half of the expired leases. - ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0)); + ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0u)); + ASSERT_EQ(static_cast(saved_expired_leases.size() / 2u), + expired_leases.size()); // Make sure that returned leases are those that are not reclaimed, i.e. // those that have even index. From 7b2bf0c844236f8425844cbb45a9e2b4ee05140e Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 25 May 2018 15:22:11 +0300 Subject: [PATCH 08/29] minor changes --- src/lib/dhcpsrv/cql_host_data_source.h | 1 + src/lib/dhcpsrv/cql_lease_mgr.h | 1 + src/lib/dhcpsrv/database_backends.dox | 2 ++ 3 files changed, 4 insertions(+) diff --git a/src/lib/dhcpsrv/cql_host_data_source.h b/src/lib/dhcpsrv/cql_host_data_source.h index f04f50c25b..86915422d3 100644 --- a/src/lib/dhcpsrv/cql_host_data_source.h +++ b/src/lib/dhcpsrv/cql_host_data_source.h @@ -68,6 +68,7 @@ class CqlHostDataSource : public BaseHostDataSource { /// - request-timeout /// - tcp-keepalive /// - tcp-nodelay + /// - max-statement-tries /// /// For details regarding those paraemters, see /// @ref isc::dhcp::CqlConnection::openDatabase. diff --git a/src/lib/dhcpsrv/cql_lease_mgr.h b/src/lib/dhcpsrv/cql_lease_mgr.h index 3c0cc1ba75..0bab4fb4a2 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.h +++ b/src/lib/dhcpsrv/cql_lease_mgr.h @@ -59,6 +59,7 @@ class CqlLeaseMgr : public LeaseMgr { /// - request-timeout (12000) /// - tcp-keepalive (no) /// - tcp-nodelay (no) + /// - max-statement-tries (1) /// /// Finally, all the CQL commands are pre-compiled. /// diff --git a/src/lib/dhcpsrv/database_backends.dox b/src/lib/dhcpsrv/database_backends.dox index 59d73082cd..cff84dc575 100644 --- a/src/lib/dhcpsrv/database_backends.dox +++ b/src/lib/dhcpsrv/database_backends.dox @@ -102,6 +102,8 @@ - password - an optional password if required for connection - keyspace - an optional keyspace. If not specified, the default value of 'keatest' will be used. + - max-statement-tries - maximum number of tries for a Cassandra + statement before throwing exception, default: 1 For details, see @ref isc::dhcp::CqlConnection::openDatabase(). From 03d7eb650526870e22e3462b4948659ebf024db5 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 25 May 2018 16:33:10 +0300 Subject: [PATCH 09/29] minor changes --- src/bin/dhcp6/tests/callout_library_3.cc | 2 ++ src/hooks/dhcp/stat_cmds/stat_cmds_callouts.cc | 2 ++ src/hooks/dhcp/stat_cmds/stat_cmds_log.cc | 2 ++ src/hooks/dhcp/stat_cmds/tests/run_unittests.cc | 2 ++ src/hooks/dhcp/stat_cmds/tests/stat_cmds_unittest.cc | 2 ++ 5 files changed, 10 insertions(+) diff --git a/src/bin/dhcp6/tests/callout_library_3.cc b/src/bin/dhcp6/tests/callout_library_3.cc index 3305ac1ce6..b268aedde1 100644 --- a/src/bin/dhcp6/tests/callout_library_3.cc +++ b/src/bin/dhcp6/tests/callout_library_3.cc @@ -9,6 +9,8 @@ /// hook point. /// static const int LIBRARY_NUMBER = 3; +#include + #include #include #include diff --git a/src/hooks/dhcp/stat_cmds/stat_cmds_callouts.cc b/src/hooks/dhcp/stat_cmds/stat_cmds_callouts.cc index 768ff05d00..be210486e5 100644 --- a/src/hooks/dhcp/stat_cmds/stat_cmds_callouts.cc +++ b/src/hooks/dhcp/stat_cmds/stat_cmds_callouts.cc @@ -8,6 +8,8 @@ // mangling that accompanies use of the C++ compiler as well as to avoid // issues related to namespaces. +#include + #include #include #include diff --git a/src/hooks/dhcp/stat_cmds/stat_cmds_log.cc b/src/hooks/dhcp/stat_cmds/stat_cmds_log.cc index 8b257da7b3..1b985a6640 100644 --- a/src/hooks/dhcp/stat_cmds/stat_cmds_log.cc +++ b/src/hooks/dhcp/stat_cmds/stat_cmds_log.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include namespace isc { diff --git a/src/hooks/dhcp/stat_cmds/tests/run_unittests.cc b/src/hooks/dhcp/stat_cmds/tests/run_unittests.cc index 096485fb7a..f5014ce44f 100644 --- a/src/hooks/dhcp/stat_cmds/tests/run_unittests.cc +++ b/src/hooks/dhcp/stat_cmds/tests/run_unittests.cc @@ -4,6 +4,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include diff --git a/src/hooks/dhcp/stat_cmds/tests/stat_cmds_unittest.cc b/src/hooks/dhcp/stat_cmds/tests/stat_cmds_unittest.cc index 4e8a8a0489..46d1e54595 100644 --- a/src/hooks/dhcp/stat_cmds/tests/stat_cmds_unittest.cc +++ b/src/hooks/dhcp/stat_cmds/tests/stat_cmds_unittest.cc @@ -6,6 +6,8 @@ // +#include + #include #include #include From a580e067e4571b25064d1b86c2a37efa9d5f9fe0 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Tue, 29 May 2018 16:14:04 +0300 Subject: [PATCH 10/29] fixed unit tests --- src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc | 4 ++-- src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 54a5fb0bb0..594bcbc187 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -1076,7 +1076,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configReloadMissingFile) { sendUnixCommand("{ \"command\": \"config-reload\" }", response); // Verify the reload was rejected. - EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed:" + EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed: " "configuration error using file 'test6.json': Unable to open file " "test6.json\" }", response); @@ -1102,7 +1102,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configReloadBrokenFile) { sendUnixCommand("{ \"command\": \"config-reload\" }", response); // Verify the reload will fail. - EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed:" + EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed: " "configuration error using file 'test7.json': " "test7.json:1.1: Invalid character: g\" }", response); diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index e6fcb40ab4..ef497bdfc4 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -1098,7 +1098,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configReloadMissingFile) { sendUnixCommand("{ \"command\": \"config-reload\" }", response); // Verify the reload was rejected. - EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed:" + EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed: " "configuration error using file 'test6.json': Unable to open file " "test6.json\" }", response); @@ -1124,7 +1124,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configReloadBrokenFile) { sendUnixCommand("{ \"command\": \"config-reload\" }", response); // Verify the reload will fail. - EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed:" + EXPECT_EQ("{ \"result\": 1, \"text\": \"Config reload failed: " "configuration error using file 'test7.json': " "test7.json:1.1: Invalid character: g\" }", response); From 96ee8aee88312f2d1cd6f048899266fe9c426781 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Tue, 29 May 2018 18:08:09 +0300 Subject: [PATCH 11/29] fixed unit tests --- src/bin/admin/kea-admin.in | 6 +++--- src/bin/admin/tests/pgsql_tests.sh.in | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 0687b401cd..27cdca33ce 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -635,7 +635,7 @@ pgsql_upgrade() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export PGPASSWORD=$db_password + export $db_password for script in ${scripts_dir}/pgsql/upgrade*.sh do @@ -667,7 +667,7 @@ pgsql_config_upgrade() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export PGPASSWORD=$db_password + export $db_password for script in ${scripts_dir}/pgsql/config_upgrade*.sh do @@ -699,7 +699,7 @@ pgsql_master_upgrade() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export PGPASSWORD=$db_password + export $db_password for script in ${scripts_dir}/pgsql/master_upgrade*.sh do diff --git a/src/bin/admin/tests/pgsql_tests.sh.in b/src/bin/admin/tests/pgsql_tests.sh.in index ef0854ef25..4f7f65755d 100644 --- a/src/bin/admin/tests/pgsql_tests.sh.in +++ b/src/bin/admin/tests/pgsql_tests.sh.in @@ -488,7 +488,7 @@ pgsql_upgrade_schema_to_version() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export PGPASSWORD=$db_password + export $db_password for script in ${db_scripts_dir}/pgsql/upgrade*.sh do From 08f81baae8f695964ea687e2a8a0c7934482bef5 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Tue, 5 Jun 2018 12:52:59 +0300 Subject: [PATCH 12/29] minor changes --- src/bin/admin/kea-admin.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 27cdca33ce..c47e5f2b39 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -1030,7 +1030,7 @@ mysql_create_database_and_users() { } pgsql_create_database_and_users() { - export PGPASSWORD=${db_password} + export ${db_password} query="DO \$body\$ BEGIN From 7797cd5df404acce95aaa089d530697c726ec6f3 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Tue, 5 Jun 2018 15:12:10 +0300 Subject: [PATCH 13/29] fixed typo --- src/bin/admin/kea-admin.in | 8 ++++---- src/bin/admin/tests/pgsql_tests.sh.in | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index c47e5f2b39..bb96b09dd8 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -635,7 +635,7 @@ pgsql_upgrade() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export $db_password + export db_password for script in ${scripts_dir}/pgsql/upgrade*.sh do @@ -667,7 +667,7 @@ pgsql_config_upgrade() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export $db_password + export db_password for script in ${scripts_dir}/pgsql/config_upgrade*.sh do @@ -699,7 +699,7 @@ pgsql_master_upgrade() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export $db_password + export db_password for script in ${scripts_dir}/pgsql/master_upgrade*.sh do @@ -1030,7 +1030,7 @@ mysql_create_database_and_users() { } pgsql_create_database_and_users() { - export ${db_password} + export db_password query="DO \$body\$ BEGIN diff --git a/src/bin/admin/tests/pgsql_tests.sh.in b/src/bin/admin/tests/pgsql_tests.sh.in index 4f7f65755d..16f8d1de33 100644 --- a/src/bin/admin/tests/pgsql_tests.sh.in +++ b/src/bin/admin/tests/pgsql_tests.sh.in @@ -488,7 +488,7 @@ pgsql_upgrade_schema_to_version() { # Postgres psql does not accept pw on command line, but can do it # thru an env - export $db_password + export db_password for script in ${db_scripts_dir}/pgsql/upgrade*.sh do From 81c29de4e03f4f12c146584ad94567fda7d3ee5e Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Mon, 11 Jun 2018 14:22:41 +0300 Subject: [PATCH 14/29] minor changes --- .../tests/command_options_helper.h | 2 +- src/lib/dhcpsrv/cfg_srv_config_type.h | 8 +- src/lib/dhcpsrv/cql_srv_config_master_mgr.cc | 32 +- src/lib/dhcpsrv/cql_srv_config_master_mgr.h | 6 +- src/lib/dhcpsrv/cql_srv_config_mgr.cc | 284 +++++++----------- src/lib/dhcpsrv/cql_srv_config_mgr.h | 9 +- .../dhcpsrv/mysql_srv_config_master_mgr.cc | 43 ++- src/lib/dhcpsrv/mysql_srv_config_master_mgr.h | 6 +- src/lib/dhcpsrv/mysql_srv_config_mgr.h | 20 +- .../dhcpsrv/pgsql_srv_config_master_mgr.cc | 21 +- src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h | 6 +- src/lib/dhcpsrv/pgsql_srv_config_mgr.h | 20 +- src/lib/dhcpsrv/srv_config_master_mgr.h | 22 +- .../dhcpsrv/srv_config_master_mgr_factory.cc | 18 +- src/lib/dhcpsrv/srv_config_mgr.h | 2 +- src/lib/dhcpsrv/srv_config_mgr_factory.cc | 21 +- src/lib/dhcpsrv/srv_config_mgr_factory.h | 2 +- 17 files changed, 216 insertions(+), 306 deletions(-) diff --git a/src/bin/kea_config_tool/tests/command_options_helper.h b/src/bin/kea_config_tool/tests/command_options_helper.h index 6675f5f102..2e6e01b91c 100644 --- a/src/bin/kea_config_tool/tests/command_options_helper.h +++ b/src/bin/kea_config_tool/tests/command_options_helper.h @@ -84,7 +84,7 @@ class CommandOptionsHelper { public: char** argv_; ///< array of C-strings being wrapped. - int argc_; ///< number of C-strings. + int argc_; ///< number of C-strings. }; /// \brief Parse command line provided as string. diff --git a/src/lib/dhcpsrv/cfg_srv_config_type.h b/src/lib/dhcpsrv/cfg_srv_config_type.h index 90b831f075..26c024416b 100644 --- a/src/lib/dhcpsrv/cfg_srv_config_type.h +++ b/src/lib/dhcpsrv/cfg_srv_config_type.h @@ -31,11 +31,11 @@ class CfgSrvConfigType { UNKNOWN = 0, // the configuration is read from the provided configuration file FILE = 1, - //the configuration is read from the master database specified in the - //provided configuration file + // the configuration is read from the master database specified in the + // provided configuration file MASTER_DATABASE = 2, - //the configuration is read from a configuration database specified in the - //provided configuration file. There is no need of master database + // the configuration is read from a configuration database specified in the + // provided configuration file. There is no need of master database CONFIG_DATABASE = 3 }; diff --git a/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc index 1e29d4ddc3..6d7b46b741 100644 --- a/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc +++ b/src/lib/dhcpsrv/cql_srv_config_master_mgr.cc @@ -23,13 +23,13 @@ #include -#include // for int32_t +#include // for int32_t namespace isc { namespace dhcp { StatementMap CqlMasterConfigVersionExchange::tagged_statements_ = { - {GET_VERSION, // + {GET_VERSION, // {GET_VERSION, // "SELECT " "version, minor " @@ -153,7 +153,7 @@ constexpr StatementTag CqlMasterConfigExchange::GET_SERVERS_CONFIG4_SHARDS_NAME; constexpr StatementTag CqlMasterConfigExchange::GET_SERVERS_CONFIG6_SHARDS_NAME; StatementMap CqlMasterConfigExchange::tagged_statements_ = { - {INSERT_SERVER_CONFIG4, // + {INSERT_SERVER_CONFIG4, // {INSERT_SERVER_CONFIG4, // "INSERT INTO server_configuration4 " "(instance_id, timestamp, server_config, config_database, config_database_name) " @@ -161,7 +161,7 @@ StatementMap CqlMasterConfigExchange::tagged_statements_ = { "IF NOT EXISTS " }}, - {INSERT_SERVER_CONFIG6, // + {INSERT_SERVER_CONFIG6, // {INSERT_SERVER_CONFIG6, // "INSERT INTO server_configuration6 " "(instance_id, timestamp, server_config, config_database, config_database_name) " @@ -169,81 +169,81 @@ StatementMap CqlMasterConfigExchange::tagged_statements_ = { "IF NOT EXISTS " }}, - {GET_CONFIGURATION4_BY_SRV_ID, // + {GET_CONFIGURATION4_BY_SRV_ID, // {GET_CONFIGURATION4_BY_SRV_ID, // "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration4 " "WHERE instance_id = ? " }}, - {GET_CONFIGURATION6_BY_SRV_ID, // + {GET_CONFIGURATION6_BY_SRV_ID, // {GET_CONFIGURATION6_BY_SRV_ID, // "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration6 " "WHERE instance_id = ? " }}, - {GET_CONFIGURATION4_BY_SHARD_DB, // + {GET_CONFIGURATION4_BY_SHARD_DB, // {GET_CONFIGURATION4_BY_SHARD_DB, // "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration4 " "WHERE config_database_name = ? ALLOW FILTERING " }}, - {GET_CONFIGURATION6_BY_SHARD_DB, // + {GET_CONFIGURATION6_BY_SHARD_DB, // {GET_CONFIGURATION6_BY_SHARD_DB, // "SELECT instance_id, timestamp, server_config, config_database, config_database_name " "FROM server_configuration6 " "WHERE config_database_name = ? ALLOW FILTERING " }}, - {GET_CONFIGURATION4_TIMESTAMP, // + {GET_CONFIGURATION4_TIMESTAMP, // {GET_CONFIGURATION4_TIMESTAMP, // "SELECT instance_id, timestamp " "FROM server_configuration4 " "WHERE instance_id = ? " }}, - {GET_CONFIGURATION6_TIMESTAMP, // + {GET_CONFIGURATION6_TIMESTAMP, // {GET_CONFIGURATION6_TIMESTAMP, // "SELECT instance_id, timestamp " "FROM server_configuration6 " "WHERE instance_id = ? " }}, - {GET_SERVERS_CONFIG4, // + {GET_SERVERS_CONFIG4, // {GET_SERVERS_CONFIG4, // "SELECT instance_id " "FROM server_configuration4 " }}, - {GET_SERVERS_CONFIG6, // + {GET_SERVERS_CONFIG6, // {GET_SERVERS_CONFIG6, // "SELECT instance_id " "FROM server_configuration6 " }}, - {DELETE_SERVER_CONFIG4, // + {DELETE_SERVER_CONFIG4, // {DELETE_SERVER_CONFIG4, // "DELETE FROM server_configuration4 " "WHERE instance_id = ? " "IF EXISTS " }}, - {DELETE_SERVER_CONFIG6, // + {DELETE_SERVER_CONFIG6, // {DELETE_SERVER_CONFIG6, // "DELETE FROM server_configuration6 " "WHERE instance_id = ? " "IF EXISTS " }}, - {GET_SERVERS_CONFIG4_SHARDS_NAME, // + {GET_SERVERS_CONFIG4_SHARDS_NAME, // {GET_SERVERS_CONFIG4_SHARDS_NAME, // "SELECT config_database_name " "FROM server_configuration4 " }}, - {GET_SERVERS_CONFIG6_SHARDS_NAME, // + {GET_SERVERS_CONFIG6_SHARDS_NAME, // {GET_SERVERS_CONFIG6_SHARDS_NAME, // "SELECT config_database_name " "FROM server_configuration6 " diff --git a/src/lib/dhcpsrv/cql_srv_config_master_mgr.h b/src/lib/dhcpsrv/cql_srv_config_master_mgr.h index 799508b2c6..cc8b15c441 100644 --- a/src/lib/dhcpsrv/cql_srv_config_master_mgr.h +++ b/src/lib/dhcpsrv/cql_srv_config_master_mgr.h @@ -111,7 +111,8 @@ class CqlSrvConfigMasterMgr : public SrvConfigMasterMgr { /// @param[out] server_configurations a list of server configurations being retrieved /// /// @return true if server configuration has been successfully retrieved, false otherwise - virtual bool getConfig4(const std::string& config_database_name, + virtual bool + getConfig4(const std::string& config_database_name, std::vector& server_configurations) const override; /// @brief Retrieves configurations of DHCPv6 servers belonging to a given @@ -121,7 +122,8 @@ class CqlSrvConfigMasterMgr : public SrvConfigMasterMgr { /// @param[out] server_configurations a list of server configurations being retrieved /// /// @return true if server configuration has been successfully retrieved, false otherwise - virtual bool getConfig6(const std::string& config_database_name, + virtual bool + getConfig6(const std::string& config_database_name, std::vector& server_configurations) const override; /// @brief Retrieves timestamp for a DHCPv4 shard server configuration. diff --git a/src/lib/dhcpsrv/cql_srv_config_mgr.cc b/src/lib/dhcpsrv/cql_srv_config_mgr.cc index 32086331da..d2bf68bd34 100644 --- a/src/lib/dhcpsrv/cql_srv_config_mgr.cc +++ b/src/lib/dhcpsrv/cql_srv_config_mgr.cc @@ -21,12 +21,12 @@ #include -#include // for NULL +#include // for NULL #include // for int32_t, int64_t -#include // for BOOST_ASSERT +#include // for BOOST_ASSERT #include // for boost::uuids::random_generator -#include // for boost::uuid +#include // for boost::uuid #include #include // for std::string @@ -35,7 +35,7 @@ namespace isc { namespace dhcp { StatementMap CqlConfigVersionExchange::tagged_statements_ = { - {GET_VERSION, // + {GET_VERSION, // {GET_VERSION, // "SELECT " "version, minor " @@ -47,35 +47,25 @@ class CqlConfigExchange : public virtual CqlExchange { /// @brief Statement tags definitions /// @{ // Get the timestamp of the v4 stored configuration - static constexpr StatementTag GET_CONFIGURATION4_TIMESTAMP = - "GET_CONFIGURATION4_TIMESTAMP"; + static constexpr StatementTag GET_CONFIGURATION4_TIMESTAMP = "GET_CONFIGURATION4_TIMESTAMP"; // Get the timestamp of the v6 stored configuration - static constexpr StatementTag GET_CONFIGURATION6_TIMESTAMP = - "GET_CONFIGURATION6_TIMESTAMP"; + static constexpr StatementTag GET_CONFIGURATION6_TIMESTAMP = "GET_CONFIGURATION6_TIMESTAMP"; // Get only GENERIC server configuration v4 - static constexpr StatementTag GET_GENERIC_CONFIGURATION4 = - "GET_GENERIC_CONFIGURATION4"; + static constexpr StatementTag GET_GENERIC_CONFIGURATION4 = "GET_GENERIC_CONFIGURATION4"; // Get only GENERIC server configuration v6 - static constexpr StatementTag GET_GENERIC_CONFIGURATION6 = - "GET_GENERIC_CONFIGURATION6"; + static constexpr StatementTag GET_GENERIC_CONFIGURATION6 = "GET_GENERIC_CONFIGURATION6"; // Get only JSON server configuration v4 - static constexpr StatementTag GET_JSON_CONFIGURATION4 = - "GET_JSON_CONFIGURATION4"; + static constexpr StatementTag GET_JSON_CONFIGURATION4 = "GET_JSON_CONFIGURATION4"; // Get only JSON server configuration v6 - static constexpr StatementTag GET_JSON_CONFIGURATION6 = - "GET_JSON_CONFIGURATION6"; + static constexpr StatementTag GET_JSON_CONFIGURATION6 = "GET_JSON_CONFIGURATION6"; // Add server configuration v4 - static constexpr StatementTag INSERT_CONFIGURATION4 = - "INSERT_CONFIGURATION4"; + static constexpr StatementTag INSERT_CONFIGURATION4 = "INSERT_CONFIGURATION4"; // Add server configuration v6 - static constexpr StatementTag INSERT_CONFIGURATION6 = - "INSERT_CONFIGURATION6"; + static constexpr StatementTag INSERT_CONFIGURATION6 = "INSERT_CONFIGURATION6"; // Update server configuration v4 - static constexpr StatementTag UPDATE_CONFIGURATION4 = - "UPDATE_CONFIGURATION4"; + static constexpr StatementTag UPDATE_CONFIGURATION4 = "UPDATE_CONFIGURATION4"; // Update server configuration v6 - static constexpr StatementTag UPDATE_CONFIGURATION6 = - "UPDATE_CONFIGURATION6"; + static constexpr StatementTag UPDATE_CONFIGURATION6 = "UPDATE_CONFIGURATION6"; /// @} /// @brief Constructor @@ -94,8 +84,7 @@ class CqlConfigExchange : public virtual CqlExchange { /// @param data array of bound objects representing data to be retrieved. /// @param statement_tag prepared statement being executed; defaults to an /// invalid index - void createBindForSelect(AnyArray& data, - StatementTag statement_tag = NULL) override; + void createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL) override; /// @brief Copy received data into @ref SrvConfigInfo object /// @@ -119,8 +108,7 @@ class CqlConfigExchange : public virtual CqlExchange { /// @param statement_tag prepared Cassandra statement being executed /// /// @return true if statement has been [applied], false otherwise. - SrvConfigInfoCollection getCommon(CqlConnection& connection, - StatementTag statement_tag); + SrvConfigInfoCollection getCommon(CqlConnection& connection, StatementTag statement_tag); /// @brief Common method of executing INSERT statements for the shard /// database in the Cassandra database. @@ -177,7 +165,7 @@ constexpr StatementTag CqlConfigExchange::UPDATE_CONFIGURATION4; constexpr StatementTag CqlConfigExchange::UPDATE_CONFIGURATION6; StatementMap CqlConfigExchange::tagged_statements_ = { - {GET_CONFIGURATION4_TIMESTAMP, // + {GET_CONFIGURATION4_TIMESTAMP, // {GET_CONFIGURATION4_TIMESTAMP, // "SELECT config_id, timestamp " "FROM server_configuration4 " @@ -185,7 +173,7 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // GET_CONFIGURATION6_TIMESTAMP - {GET_CONFIGURATION6_TIMESTAMP, // + {GET_CONFIGURATION6_TIMESTAMP, // {GET_CONFIGURATION6_TIMESTAMP, // "SELECT config_id, timestamp " "FROM server_configuration6 " @@ -193,7 +181,7 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // GET_GENERIC_CONFIGURATION4 - {GET_GENERIC_CONFIGURATION4, // + {GET_GENERIC_CONFIGURATION4, // {GET_GENERIC_CONFIGURATION4, // "SELECT config_id, timestamp, generic_data " "FROM server_configuration4 " @@ -201,14 +189,14 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // GET_GENERIC_CONFIGURATION6 - {GET_GENERIC_CONFIGURATION6, // + {GET_GENERIC_CONFIGURATION6, // {GET_GENERIC_CONFIGURATION6, // "SELECT config_id, timestamp, generic_data " "FROM server_configuration6 LIMIT 1 " }}, // GET_JSON_CONFIGURATION4 - {GET_JSON_CONFIGURATION4, // + {GET_JSON_CONFIGURATION4, // {GET_JSON_CONFIGURATION4, // "SELECT config_id, timestamp, json_data " "FROM server_configuration4 " @@ -216,7 +204,7 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // GET_JSON_CONFIGURATION6 - {GET_JSON_CONFIGURATION6, // + {GET_JSON_CONFIGURATION6, // {GET_JSON_CONFIGURATION6, // "SELECT config_id, timestamp, json_data " "FROM server_configuration6 " @@ -224,7 +212,7 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // INSERT_CONFIGURATION4 - {INSERT_CONFIGURATION4, // + {INSERT_CONFIGURATION4, // {INSERT_CONFIGURATION4, // "INSERT INTO server_configuration4 " "(config_id, timestamp, json_data, generic_data) " @@ -232,7 +220,7 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // INSERT_CONFIGURATION6 - {INSERT_CONFIGURATION6, // + {INSERT_CONFIGURATION6, // {INSERT_CONFIGURATION6, // "INSERT INTO server_configuration6 " "(config_id, timestamp, json_data, generic_data) " @@ -240,7 +228,7 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // UPDATE_CONFIGURATION4 - {UPDATE_CONFIGURATION4, // + {UPDATE_CONFIGURATION4, // {UPDATE_CONFIGURATION4, // "UPDATE server_configuration4 " "SET timestamp = ?, json_data = ?, " @@ -249,7 +237,7 @@ StatementMap CqlConfigExchange::tagged_statements_ = { }}, // UPDATE_CONFIGURATION6 - {UPDATE_CONFIGURATION6, // + {UPDATE_CONFIGURATION6, // {UPDATE_CONFIGURATION6, // "UPDATE server_configuration6 " "SET timestamp = ?, json_data = ?, " @@ -264,9 +252,8 @@ CqlConfigExchange::CqlConfigExchange() : config_data_(new SrvConfigInfo()) { CqlConfigExchange::~CqlConfigExchange() { } -void -CqlConfigExchange::createBindForSelect( - AnyArray& data, StatementTag statement_tag /* = NULL */) { +void CqlConfigExchange::createBindForSelect(AnyArray& data, + StatementTag statement_tag /* = NULL */) { // Start with a fresh array. data.clear(); @@ -281,29 +268,25 @@ CqlConfigExchange::createBindForSelect( } } -boost::any -CqlConfigExchange::retrieve() { +boost::any CqlConfigExchange::retrieve() { SrvConfigInfoPtr config(new SrvConfigInfo(*config_data_)); return config; } -std::string -CqlConfigExchange::generateUuid() { +std::string CqlConfigExchange::generateUuid() { boost::uuids::random_generator gen; boost::uuids::uuid unique_id = gen(); std::string config_id = boost::lexical_cast(unique_id); return config_id; } -SrvConfigInfoCollection -CqlConfigExchange::getCommon(CqlConnection& connection, - StatementTag statement_tag) { +SrvConfigInfoCollection CqlConfigExchange::getCommon(CqlConnection& connection, + StatementTag statement_tag) { // Bind to array. AnyArray where_values; - AnyArray collection = - executeSelect(connection, where_values, statement_tag, true); + AnyArray collection = executeSelect(connection, where_values, statement_tag, true); SrvConfigInfoCollection result; @@ -319,12 +302,11 @@ CqlConfigExchange::getCommon(CqlConnection& connection, return result; } -bool -CqlConfigExchange::insertCommon(CqlConnection& connection, - const std::string& config_id, - const std::string& json_data, - const std::string& generic_data, - StatementTag statement_tag) { +bool CqlConfigExchange::insertCommon(CqlConnection& connection, + const std::string& config_id, + const std::string& json_data, + const std::string& generic_data, + StatementTag statement_tag) { cass_int64_t config_timestamp = static_cast(time(NULL)); @@ -340,18 +322,16 @@ CqlConfigExchange::insertCommon(CqlConnection& connection, return true; } -bool -CqlConfigExchange::updateCommon(CqlConnection& connection, - const std::string& config_id, - const int64_t old_timestamp, - const std::string& json_data, - const std::string& generic_data, - StatementTag statement_tag) { +bool CqlConfigExchange::updateCommon(CqlConnection& connection, + const std::string& config_id, + const int64_t old_timestamp, + const std::string& json_data, + const std::string& generic_data, + StatementTag statement_tag) { // config_timestamp cass_int64_t config_timestamp = static_cast(time(NULL)); - cass_int64_t old_config_timestamp = - static_cast(old_timestamp); + cass_int64_t old_config_timestamp = static_cast(old_timestamp); // Bind to array. AnyArray data; @@ -366,8 +346,7 @@ CqlConfigExchange::updateCommon(CqlConnection& connection, return true; } -CqlSrvConfigMgr::CqlSrvConfigMgr( - const DatabaseConnection::ParameterMap& parameters) +CqlSrvConfigMgr::CqlSrvConfigMgr(const DatabaseConnection::ParameterMap& parameters) : SrvConfigMgr(), dbconn_(parameters) { dbconn_.openDatabase(); dbconn_.prepareStatements(CqlConfigExchange::tagged_statements_); @@ -377,13 +356,11 @@ CqlSrvConfigMgr::CqlSrvConfigMgr( CqlSrvConfigMgr::~CqlSrvConfigMgr() { } -SrvConfigInfoPtr -CqlSrvConfigMgr::getConfig4Timestamp() const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_SRV_CONFIG4_TIMESTAMP); +SrvConfigInfoPtr CqlSrvConfigMgr::getConfig4Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG4_TIMESTAMP); std::unique_ptr config_exchange(new CqlConfigExchange()); - SrvConfigInfoCollection collection = config_exchange->getCommon( - dbconn_, CqlConfigExchange::GET_CONFIGURATION4_TIMESTAMP); + SrvConfigInfoCollection collection = + config_exchange->getCommon(dbconn_, CqlConfigExchange::GET_CONFIGURATION4_TIMESTAMP); SrvConfigInfoPtr result; if (!collection.empty()) { @@ -393,13 +370,11 @@ CqlSrvConfigMgr::getConfig4Timestamp() const { return result; } -SrvConfigInfoPtr -CqlSrvConfigMgr::getConfig6Timestamp() const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_SRV_CONFIG6_TIMESTAMP); +SrvConfigInfoPtr CqlSrvConfigMgr::getConfig6Timestamp() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG6_TIMESTAMP); std::unique_ptr config_exchange(new CqlConfigExchange()); - SrvConfigInfoCollection collection = config_exchange->getCommon( - dbconn_, CqlConfigExchange::GET_CONFIGURATION6_TIMESTAMP); + SrvConfigInfoCollection collection = + config_exchange->getCommon(dbconn_, CqlConfigExchange::GET_CONFIGURATION6_TIMESTAMP); SrvConfigInfoPtr result; if (!collection.empty()) { @@ -409,13 +384,11 @@ CqlSrvConfigMgr::getConfig6Timestamp() const { return result; } -SrvConfigInfoPtr -CqlSrvConfigMgr::getGenericConfig4() const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_SRV_CONFIG4_GENERIC); +SrvConfigInfoPtr CqlSrvConfigMgr::getGenericConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG4_GENERIC); std::unique_ptr config_exchange(new CqlConfigExchange()); - SrvConfigInfoCollection collection = config_exchange->getCommon( - dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION4); + SrvConfigInfoCollection collection = + config_exchange->getCommon(dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION4); SrvConfigInfoPtr result; if (!collection.empty()) { @@ -425,13 +398,11 @@ CqlSrvConfigMgr::getGenericConfig4() const { return result; } -SrvConfigInfoPtr -CqlSrvConfigMgr::getGenericConfig6() const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_SRV_CONFIG6_GENERIC); +SrvConfigInfoPtr CqlSrvConfigMgr::getGenericConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG6_GENERIC); std::unique_ptr config_exchange(new CqlConfigExchange()); - SrvConfigInfoCollection collection = config_exchange->getCommon( - dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION6); + SrvConfigInfoCollection collection = + config_exchange->getCommon(dbconn_, CqlConfigExchange::GET_GENERIC_CONFIGURATION6); SrvConfigInfoPtr result; if (!collection.empty()) { @@ -441,13 +412,11 @@ CqlSrvConfigMgr::getGenericConfig6() const { return result; } -SrvConfigInfoPtr -CqlSrvConfigMgr::getJsonConfig4() const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_SRV_CONFIG4_JSON); +SrvConfigInfoPtr CqlSrvConfigMgr::getJsonConfig4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG4_JSON); std::unique_ptr config_exchange(new CqlConfigExchange()); - SrvConfigInfoCollection collection = config_exchange->getCommon( - dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION4); + SrvConfigInfoCollection collection = + config_exchange->getCommon(dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION4); SrvConfigInfoPtr result; if (!collection.empty()) { @@ -457,13 +426,11 @@ CqlSrvConfigMgr::getJsonConfig4() const { return result; } -SrvConfigInfoPtr -CqlSrvConfigMgr::getJsonConfig6() const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_SRV_CONFIG6_JSON); +SrvConfigInfoPtr CqlSrvConfigMgr::getJsonConfig6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_SRV_CONFIG6_JSON); std::unique_ptr config_exchange(new CqlConfigExchange()); - SrvConfigInfoCollection collection = config_exchange->getCommon( - dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION6); + SrvConfigInfoCollection collection = + config_exchange->getCommon(dbconn_, CqlConfigExchange::GET_JSON_CONFIGURATION6); SrvConfigInfoPtr result; if (!collection.empty()) { @@ -473,23 +440,18 @@ CqlSrvConfigMgr::getJsonConfig6() const { return result; } -std::string -CqlSrvConfigMgr::getType() const { +std::string CqlSrvConfigMgr::getType() const { return std::string("cql"); } -VersionPair -CqlSrvConfigMgr::getVersion() const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_VERSION); +VersionPair CqlSrvConfigMgr::getVersion() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_VERSION); - std::unique_ptr version_exchange( - new CqlVersionExchange()); + std::unique_ptr version_exchange(new CqlVersionExchange()); return version_exchange->retrieveVersion(dbconn_); } -std::string -CqlSrvConfigMgr::getDBVersion() { +std::string CqlSrvConfigMgr::getDBVersion() { std::stringstream tmp; tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR; tmp << "." << CQL_SCHEMA_VERSION_MINOR; @@ -499,12 +461,10 @@ CqlSrvConfigMgr::getDBVersion() { return tmp.str(); } -bool -CqlSrvConfigMgr::updateConfig4(const int64_t config_timestamp, - const std::string& json_data, - const std::string& generic_data) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG4); +bool CqlSrvConfigMgr::updateConfig4(const int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG4); // Initiate CQL transaction as we will have to update the config // from two steps and we don't want anybody else to write another @@ -526,13 +486,11 @@ CqlSrvConfigMgr::updateConfig4(const int64_t config_timestamp, return false; } - if (!updateConfig4(current_config->config_id_, config_timestamp, json_data, - generic_data)) { + if (!updateConfig4(current_config->config_id_, config_timestamp, json_data, generic_data)) { // The configuration timestamp has been changed since the last // configuration read until this update. - LOG_WARN(dhcpsrv_logger, - DHCPSRV_CQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED) + LOG_WARN(dhcpsrv_logger, DHCPSRV_CQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED) .arg(config_timestamp) .arg(current_config->timestamp_); @@ -542,12 +500,10 @@ CqlSrvConfigMgr::updateConfig4(const int64_t config_timestamp, return true; } -bool -CqlSrvConfigMgr::updateConfig6(const int64_t config_timestamp, - const std::string& json_data, - const std::string& generic_data) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG6); +bool CqlSrvConfigMgr::updateConfig6(const int64_t config_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG6); // Initiate CQL transaction as we will have to update the config // from two steps and we don't want anybody else to write another @@ -569,12 +525,10 @@ CqlSrvConfigMgr::updateConfig6(const int64_t config_timestamp, return false; } - if (!updateConfig6(current_config->config_id_, config_timestamp, json_data, - generic_data)) { + if (!updateConfig6(current_config->config_id_, config_timestamp, json_data, generic_data)) { // The configuration timestamp has been changed since the last // configuration read until this update. - LOG_WARN(dhcpsrv_logger, - DHCPSRV_CQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED) + LOG_WARN(dhcpsrv_logger, DHCPSRV_CQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED) .arg(config_timestamp) .arg(current_config->timestamp_); @@ -584,20 +538,17 @@ CqlSrvConfigMgr::updateConfig6(const int64_t config_timestamp, return true; } -bool -CqlSrvConfigMgr::insertConfig4(const std::string& json_data, - const std::string& generic_data) const { +bool CqlSrvConfigMgr::insertConfig4(const std::string& json_data, + const std::string& generic_data) const { std::unique_ptr config_exchange(new CqlConfigExchange()); const std::string config_id = config_exchange->generateUuid(); - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_INSERT_SRV_CONFIG4) + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_INSERT_SRV_CONFIG4) .arg(config_id); try { - config_exchange->insertCommon(dbconn_, config_id, json_data, - generic_data, + config_exchange->insertCommon(dbconn_, config_id, json_data, generic_data, CqlConfigExchange::INSERT_CONFIGURATION4); } catch (const Exception&) { @@ -607,19 +558,16 @@ CqlSrvConfigMgr::insertConfig4(const std::string& json_data, return true; } -bool -CqlSrvConfigMgr::insertConfig6(const std::string& json_data, - const std::string& generic_data) const { +bool CqlSrvConfigMgr::insertConfig6(const std::string& json_data, + const std::string& generic_data) const { std::unique_ptr config_exchange(new CqlConfigExchange()); const std::string config_id = config_exchange->generateUuid(); - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_INSERT_SRV_CONFIG6) + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_INSERT_SRV_CONFIG6) .arg(config_id); try { - config_exchange->insertCommon(dbconn_, config_id, json_data, - generic_data, + config_exchange->insertCommon(dbconn_, config_id, json_data, generic_data, CqlConfigExchange::INSERT_CONFIGURATION6); } catch (const Exception&) { return false; @@ -628,19 +576,16 @@ CqlSrvConfigMgr::insertConfig6(const std::string& json_data, return true; } -bool -CqlSrvConfigMgr::updateConfig4(const std::string& config_id, - const int64_t old_timestamp, - const std::string& json_data, - const std::string& generic_data) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_UPDATE_SRV_CONFIG4) +bool CqlSrvConfigMgr::updateConfig4(const std::string& config_id, + const int64_t old_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_UPDATE_SRV_CONFIG4) .arg(config_id); std::unique_ptr config_exchange(new CqlConfigExchange()); try { - config_exchange->updateCommon(dbconn_, config_id, old_timestamp, - json_data, generic_data, + config_exchange->updateCommon(dbconn_, config_id, old_timestamp, json_data, generic_data, CqlConfigExchange::UPDATE_CONFIGURATION4); } catch (const Exception&) { return false; @@ -649,19 +594,16 @@ CqlSrvConfigMgr::updateConfig4(const std::string& config_id, return true; } -bool -CqlSrvConfigMgr::updateConfig6(const std::string& config_id, - const int64_t old_timestamp, - const std::string& json_data, - const std::string& generic_data) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_UPDATE_SRV_CONFIG6) +bool CqlSrvConfigMgr::updateConfig6(const std::string& config_id, + const int64_t old_timestamp, + const std::string& json_data, + const std::string& generic_data) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_UPDATE_SRV_CONFIG6) .arg(config_id); std::unique_ptr config_exchange(new CqlConfigExchange()); try { - config_exchange->updateCommon(dbconn_, config_id, old_timestamp, - json_data, generic_data, + config_exchange->updateCommon(dbconn_, config_id, old_timestamp, json_data, generic_data, CqlConfigExchange::UPDATE_CONFIGURATION6); } catch (const Exception&) { return false; @@ -670,22 +612,18 @@ CqlSrvConfigMgr::updateConfig6(const std::string& config_id, return true; } -bool -CqlSrvConfigMgr::startTransaction() { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_BEGIN_TRANSACTION); +bool CqlSrvConfigMgr::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_BEGIN_TRANSACTION); dbconn_.startTransaction(); return true; } -void -CqlSrvConfigMgr::commit() { +void CqlSrvConfigMgr::commit() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT); dbconn_.commit(); } -void -CqlSrvConfigMgr::rollback() { +void CqlSrvConfigMgr::rollback() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK); dbconn_.rollback(); } diff --git a/src/lib/dhcpsrv/cql_srv_config_mgr.h b/src/lib/dhcpsrv/cql_srv_config_mgr.h index 9f48d4527a..65a5283a2b 100644 --- a/src/lib/dhcpsrv/cql_srv_config_mgr.h +++ b/src/lib/dhcpsrv/cql_srv_config_mgr.h @@ -35,8 +35,7 @@ class CqlConfigExchange; class CqlSrvConfigMgr : public SrvConfigMgr { public: /// @brief Constructor - explicit CqlSrvConfigMgr( - const DatabaseConnection::ParameterMap& parameters); + explicit CqlSrvConfigMgr(const DatabaseConnection::ParameterMap& parameters); /// @brief Destructor virtual ~CqlSrvConfigMgr(); @@ -133,8 +132,7 @@ class CqlSrvConfigMgr : public SrvConfigMgr { /// CQL error. /// /// @return true if the configuration was added, false if not. - bool insertConfig4(const std::string& json_data, - const std::string& generic_data) const; + bool insertConfig4(const std::string& json_data, const std::string& generic_data) const; /// @brief Inserts a new DHCPv6 server configuration. /// @@ -148,8 +146,7 @@ class CqlSrvConfigMgr : public SrvConfigMgr { /// CQL error. /// /// @return true if the configuration was added, false if not. - bool insertConfig6(const std::string& json_data, - const std::string& generic_data) const; + bool insertConfig6(const std::string& json_data, const std::string& generic_data) const; /// @brief Updates an existing DHCPv4 server configuration. /// diff --git a/src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc index f3046f79d1..2026e8252e 100644 --- a/src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc +++ b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.cc @@ -71,33 +71,26 @@ std::array tagged_stat " FROM server_configuration6" " WHERE config_database_name = ?"}, - {MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_TIMESTAMP, - "SELECT instance_id, timestamp" - " FROM server_configuration4" - " WHERE instance_id = ? "}, + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION4_TIMESTAMP, "SELECT instance_id, timestamp" + " FROM server_configuration4" + " WHERE instance_id = ? "}, - {MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_TIMESTAMP, - "SELECT instance_id, timestamp" - " FROM server_configuration6" - " WHERE instance_id = ? "}, + {MySqlSrvConfigMasterMgr::GET_CONFIGURATION6_TIMESTAMP, "SELECT instance_id, timestamp" + " FROM server_configuration6" + " WHERE instance_id = ? "}, - {MySqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG4, - "TRUNCATE server_configuration4 "}, + {MySqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG4, "TRUNCATE server_configuration4 "}, - {MySqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG6, - "TRUNCATE server_configuration6 "}, + {MySqlSrvConfigMasterMgr::DELETE_SERVER_CONFIG6, "TRUNCATE server_configuration6 "}, - {MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG4_SHARDS_NAME, - "SELECT config_database_name" - " FROM server_configuration4"}, + {MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG4_SHARDS_NAME, "SELECT config_database_name" + " FROM server_configuration4"}, - {MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG6_SHARDS_NAME, - "SELECT config_database_name" - " FROM server_configuration6"}, + {MySqlSrvConfigMasterMgr::GET_SERVERS_CONFIG6_SHARDS_NAME, "SELECT config_database_name" + " FROM server_configuration6"}, - {MySqlSrvConfigMasterMgr::GET_VERSION, - "SELECT version, minor" - " FROM master_schema_version"}, + {MySqlSrvConfigMasterMgr::GET_VERSION, "SELECT version, minor" + " FROM master_schema_version"}, }}; } // namespace @@ -512,8 +505,8 @@ bool MySqlSrvConfigMasterMgr::addServerConfig4(const std::string& instance_id, .arg(config_database_name); const StatementIndex statement_index = MySqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG4; - return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, - config_database_name); + return addCommonServerConfiguration(statement_index, instance_id, server_config, + config_database, config_database_name); } bool MySqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, @@ -525,8 +518,8 @@ bool MySqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, .arg(config_database_name); const StatementIndex statement_index = MySqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG6; - return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, - config_database_name); + return addCommonServerConfiguration(statement_index, instance_id, server_config, + config_database, config_database_name); } bool MySqlSrvConfigMasterMgr::clearServersConfig4() const { diff --git a/src/lib/dhcpsrv/mysql_srv_config_master_mgr.h b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.h index 1c11b34136..4d3fd30132 100644 --- a/src/lib/dhcpsrv/mysql_srv_config_master_mgr.h +++ b/src/lib/dhcpsrv/mysql_srv_config_master_mgr.h @@ -143,7 +143,8 @@ class MySqlSrvConfigMasterMgr : public SrvConfigMasterMgr { /// @param[out] server_configurations a list of server configurations being retrieved /// /// @return true if server configuration has been successfully retrieved, false otherwise - virtual bool getConfig4(const std::string& config_database_name, + virtual bool + getConfig4(const std::string& config_database_name, std::vector& server_configurations) const override; /// @brief Retrieves configurations of DHCPv6 servers belonging to a given @@ -153,7 +154,8 @@ class MySqlSrvConfigMasterMgr : public SrvConfigMasterMgr { /// @param[out] server_configurations a list of server configurations being retrieved /// /// @return true if server configuration has been successfully retrieved, false otherwise - virtual bool getConfig6(const std::string& config_database_name, + virtual bool + getConfig6(const std::string& config_database_name, std::vector& server_configurations) const override; /// @brief Retrieves timestamp for a DHCPv4 shard server configuration. diff --git a/src/lib/dhcpsrv/mysql_srv_config_mgr.h b/src/lib/dhcpsrv/mysql_srv_config_mgr.h index 3c2224c62f..301cfca7eb 100644 --- a/src/lib/dhcpsrv/mysql_srv_config_mgr.h +++ b/src/lib/dhcpsrv/mysql_srv_config_mgr.h @@ -181,20 +181,20 @@ class MySqlSrvConfigMgr : public SrvConfigMgr { /// /// The contents of the enum are indexes into the list of SQL statements enum StatementIndex { - GET_VERSION, // Obtain version number + GET_VERSION, // Obtain version number GET_CONFIGURATION4_TIMESTAMP, // Get the timestamp of the v4 stored // configuration GET_CONFIGURATION6_TIMESTAMP, // Get the timestamp of the v6 stored // configuration - GET_JSON_CONFIGURATION4, // Get only JSON server configuration v4 - GET_JSON_CONFIGURATION6, // Get only JSON server configuration v6 - GET_GENERIC_CONFIGURATION4, // Get only GENERIC server configuration v4 - GET_GENERIC_CONFIGURATION6, // Get only GENERIC server configuration v6 - INSERT_CONFIGURATION4, // Add server configuration v4 - INSERT_CONFIGURATION6, // Add server configuration v6 - UPDATE_CONFIGURATION4, // Update server configuration v4 - UPDATE_CONFIGURATION6, // Update server configuration v6 - NUM_STATEMENTS // Number of statements + GET_JSON_CONFIGURATION4, // Get only JSON server configuration v4 + GET_JSON_CONFIGURATION6, // Get only JSON server configuration v6 + GET_GENERIC_CONFIGURATION4, // Get only GENERIC server configuration v4 + GET_GENERIC_CONFIGURATION6, // Get only GENERIC server configuration v6 + INSERT_CONFIGURATION4, // Add server configuration v4 + INSERT_CONFIGURATION6, // Add server configuration v6 + UPDATE_CONFIGURATION4, // Update server configuration v4 + UPDATE_CONFIGURATION6, // Update server configuration v6 + NUM_STATEMENTS // Number of statements }; private: diff --git a/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc index 57702b8b38..ba5aa480e0 100644 --- a/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.cc @@ -105,16 +105,10 @@ std::array tagged " WHERE instance_id = $1"}, // DELETE_SERVER_CONFIG4 - {0, - {OID_NONE}, - "DELETE_SERVER_CONFIG4", - "TRUNCATE server_configuration4"}, + {0, {OID_NONE}, "DELETE_SERVER_CONFIG4", "TRUNCATE server_configuration4"}, // DELETE_SERVER_CONFIG6, - {0, - {OID_NONE}, - "DELETE_SERVER_CONFIG6", - "TRUNCATE server_configuration6"}, + {0, {OID_NONE}, "DELETE_SERVER_CONFIG6", "TRUNCATE server_configuration6"}, // GET_SERVERS_CONFIG4_SHARDS_NAME {0, @@ -200,7 +194,8 @@ class PgSqlMasterConfigExchange : public PgSqlExchange { } catch (const std::exception& ex) { isc_throw(DbOperationError, "Could not create bind array for " "server_configuration[4|6] with server ID: " - << instance_id << ", shard name: " << config_database_name + << instance_id + << ", shard name: " << config_database_name << ", server configuration: " << server_config << ", reason: " << ex.what()); } @@ -340,8 +335,8 @@ bool PgSqlSrvConfigMasterMgr::addServerConfig4(const std::string& instance_id, .arg(config_database_name); const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG4; - return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, - config_database_name); + return addCommonServerConfiguration(statement_index, instance_id, server_config, + config_database, config_database_name); } bool PgSqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, @@ -353,8 +348,8 @@ bool PgSqlSrvConfigMasterMgr::addServerConfig6(const std::string& instance_id, .arg(config_database_name); const StatementIndex statement_index = PgSqlSrvConfigMasterMgr::INSERT_SERVER_CONFIG6; - return addCommonServerConfiguration(statement_index, instance_id, server_config, config_database, - config_database_name); + return addCommonServerConfiguration(statement_index, instance_id, server_config, + config_database, config_database_name); } bool PgSqlSrvConfigMasterMgr::clearServersConfig4() const { diff --git a/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h index 9f44c2d640..08a1ad336c 100644 --- a/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h +++ b/src/lib/dhcpsrv/pgsql_srv_config_master_mgr.h @@ -138,7 +138,8 @@ class PgSqlSrvConfigMasterMgr : public SrvConfigMasterMgr { /// @param[out] server_configurations a list of server configurations being retrieved /// /// @return true if server configuration has been successfully retrieved, false otherwise - virtual bool getConfig4(const std::string& config_database_name, + virtual bool + getConfig4(const std::string& config_database_name, std::vector& server_configurations) const override; /// @brief Retrieves configurations of DHCPv6 servers belonging to a given @@ -148,7 +149,8 @@ class PgSqlSrvConfigMasterMgr : public SrvConfigMasterMgr { /// @param[out] server_configurations a list of server configurations being retrieved /// /// @return true if server configuration has been successfully retrieved, false otherwise - virtual bool getConfig6(const std::string& config_database_name, + virtual bool + getConfig6(const std::string& config_database_name, std::vector& server_configurations) const override; /// @brief Retrieves timestamp for a DHCPv4 shard server configuration. diff --git a/src/lib/dhcpsrv/pgsql_srv_config_mgr.h b/src/lib/dhcpsrv/pgsql_srv_config_mgr.h index 952d095455..f8c729f03d 100644 --- a/src/lib/dhcpsrv/pgsql_srv_config_mgr.h +++ b/src/lib/dhcpsrv/pgsql_srv_config_mgr.h @@ -170,20 +170,20 @@ class PgSqlSrvConfigMgr : public SrvConfigMgr { /// /// The contents of the enum are indexes into the list of SQL statements enum StatementIndex { - GET_VERSION, // Obtain version number + GET_VERSION, // Obtain version number GET_CONFIGURATION4_TIMESTAMP, // Get the timestamp of the v4 stored // configuration GET_CONFIGURATION6_TIMESTAMP, // Get the timestamp of the v6 stored // configuration - GET_JSON_CONFIGURATION4, // Get only JSON server configuration v4 - GET_JSON_CONFIGURATION6, // Get only JSON server configuration v6 - GET_GENERIC_CONFIGURATION4, // Get only GENERIC server configuration v4 - GET_GENERIC_CONFIGURATION6, // Get only GENERIC server configuration v6 - INSERT_CONFIGURATION4, // Add server configuration v4 - INSERT_CONFIGURATION6, // Add server configuration v6 - UPDATE_CONFIGURATION4, // Update server configuration v4 - UPDATE_CONFIGURATION6, // Update server configuration v6 - NUM_STATEMENTS // Number of statements + GET_JSON_CONFIGURATION4, // Get only JSON server configuration v4 + GET_JSON_CONFIGURATION6, // Get only JSON server configuration v6 + GET_GENERIC_CONFIGURATION4, // Get only GENERIC server configuration v4 + GET_GENERIC_CONFIGURATION6, // Get only GENERIC server configuration v6 + INSERT_CONFIGURATION4, // Add server configuration v4 + INSERT_CONFIGURATION6, // Add server configuration v6 + UPDATE_CONFIGURATION4, // Update server configuration v4 + UPDATE_CONFIGURATION6, // Update server configuration v6 + NUM_STATEMENTS // Number of statements }; private: diff --git a/src/lib/dhcpsrv/srv_config_master_mgr.h b/src/lib/dhcpsrv/srv_config_master_mgr.h index 83ab6b292e..63f23619f4 100644 --- a/src/lib/dhcpsrv/srv_config_master_mgr.h +++ b/src/lib/dhcpsrv/srv_config_master_mgr.h @@ -95,8 +95,7 @@ class SrvConfigMasterMgr { /// /// @return smart pointer to the configuration (or NULL if a configuration /// is not found) - virtual SrvConfigMasterInfoPtr - getConfig4(const std::string& instance_id) const = 0; + virtual SrvConfigMasterInfoPtr getConfig4(const std::string& instance_id) const = 0; /// @brief Returns DHCPv6 instance configuration information /// @@ -104,16 +103,13 @@ class SrvConfigMasterMgr { /// /// @return smart pointer to the configuration (or NULL if a configuration /// is not found) - virtual SrvConfigMasterInfoPtr - getConfig6(const std::string& instance_id) const = 0; + virtual SrvConfigMasterInfoPtr getConfig6(const std::string& instance_id) const = 0; - virtual bool - getConfig4(const std::string& config_database_name, - std::vector& serverInfo) const = 0; + virtual bool getConfig4(const std::string& config_database_name, + std::vector& serverInfo) const = 0; - virtual bool - getConfig6(const std::string& config_database_name, - std::vector& serverInfo) const = 0; + virtual bool getConfig6(const std::string& config_database_name, + std::vector& serverInfo) const = 0; virtual SrvConfigMasterInfoPtr getMasterConfig4Timestamp(const std::string& instance_id) const = 0; @@ -121,11 +117,9 @@ class SrvConfigMasterMgr { virtual SrvConfigMasterInfoPtr getMasterConfig6Timestamp(const std::string& instance_id) const = 0; - virtual bool - getServersConfig4ShardsName(std::set& shards_list) const = 0; + virtual bool getServersConfig4ShardsName(std::set& shards_list) const = 0; - virtual bool - getServersConfig6ShardsName(std::set& shards_list) const = 0; + virtual bool getServersConfig6ShardsName(std::set& shards_list) const = 0; /// @brief Start Transaction /// diff --git a/src/lib/dhcpsrv/srv_config_master_mgr_factory.cc b/src/lib/dhcpsrv/srv_config_master_mgr_factory.cc index bb0fad5337..3ce76eeee2 100644 --- a/src/lib/dhcpsrv/srv_config_master_mgr_factory.cc +++ b/src/lib/dhcpsrv/srv_config_master_mgr_factory.cc @@ -34,19 +34,16 @@ namespace isc { namespace dhcp { -boost::scoped_ptr& -SrvConfigMasterMgrFactory::getConfigurationMgrPtr() { +boost::scoped_ptr& SrvConfigMasterMgrFactory::getConfigurationMgrPtr() { static boost::scoped_ptr configurationMgrPtr; return (configurationMgrPtr); } -void -SrvConfigMasterMgrFactory::create(const std::string& dbaccess) { +void SrvConfigMasterMgrFactory::create(const std::string& dbaccess) { const std::string type = "type"; // Parse the access string and create a redacted string for logging. - DatabaseConnection::ParameterMap parameters = - DatabaseConnection::parse(dbaccess); + DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(dbaccess); std::string redacted = DatabaseConnection::redactedAccessString(parameters); // Is "type" present? @@ -85,8 +82,7 @@ SrvConfigMasterMgrFactory::create(const std::string& dbaccess) { "not specify a supported database backend"); } -void -SrvConfigMasterMgrFactory::destroy() { +void SrvConfigMasterMgrFactory::destroy() { // Destroy current lease manager. This is a no-op if no lease manager // is available. if (getConfigurationMgrPtr()) { @@ -96,8 +92,7 @@ SrvConfigMasterMgrFactory::destroy() { getConfigurationMgrPtr().reset(); } -SrvConfigMasterMgr& -SrvConfigMasterMgrFactory::instance() { +SrvConfigMasterMgr& SrvConfigMasterMgrFactory::instance() { SrvConfigMasterMgr* cfgptr = getConfigurationMgrPtr().get(); if (cfgptr == NULL) { isc_throw(NoServerConfigMasterManager, @@ -106,8 +101,7 @@ SrvConfigMasterMgrFactory::instance() { return (*cfgptr); } -bool -SrvConfigMasterMgrFactory::initialized() { +bool SrvConfigMasterMgrFactory::initialized() { SrvConfigMasterMgr* cfgptr = getConfigurationMgrPtr().get(); return (cfgptr != NULL); } diff --git a/src/lib/dhcpsrv/srv_config_mgr.h b/src/lib/dhcpsrv/srv_config_mgr.h index a05d0f0050..64c3ce0cf5 100644 --- a/src/lib/dhcpsrv/srv_config_mgr.h +++ b/src/lib/dhcpsrv/srv_config_mgr.h @@ -187,4 +187,4 @@ class SrvConfigMgr { } // namespace dhcp } // namespace isc -#endif // SRV_CONFIG_MGR_H +#endif // SRV_CONFIG_MGR_H diff --git a/src/lib/dhcpsrv/srv_config_mgr_factory.cc b/src/lib/dhcpsrv/srv_config_mgr_factory.cc index 5825e1af8f..ee52962bdf 100644 --- a/src/lib/dhcpsrv/srv_config_mgr_factory.cc +++ b/src/lib/dhcpsrv/srv_config_mgr_factory.cc @@ -44,19 +44,16 @@ using namespace std; namespace isc { namespace dhcp { -boost::scoped_ptr& -SrvConfigMgrFactory::getConfigurationMgrPtr() { +boost::scoped_ptr& SrvConfigMgrFactory::getConfigurationMgrPtr() { static boost::scoped_ptr configurationMgrPtr; return (configurationMgrPtr); } -void -SrvConfigMgrFactory::create(const std::string& dbaccess) { +void SrvConfigMgrFactory::create(const std::string& dbaccess) { const std::string type = "type"; // Parse the access string and create a redacted string for logging. - DatabaseConnection::ParameterMap parameters = - DatabaseConnection::parse(dbaccess); + DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(dbaccess); std::string redacted = DatabaseConnection::redactedAccessString(parameters); // Is "type" present? @@ -95,8 +92,7 @@ SrvConfigMgrFactory::create(const std::string& dbaccess) { "not specify a supported database backend"); } -void -SrvConfigMgrFactory::destroy() { +void SrvConfigMgrFactory::destroy() { // Destroy current lease manager. This is a no-op if no lease manager // is available. if (getConfigurationMgrPtr()) { @@ -106,18 +102,15 @@ SrvConfigMgrFactory::destroy() { getConfigurationMgrPtr().reset(); } -SrvConfigMgr& -SrvConfigMgrFactory::instance() { +SrvConfigMgr& SrvConfigMgrFactory::instance() { SrvConfigMgr* cfgptr = getConfigurationMgrPtr().get(); if (cfgptr == NULL) { - isc_throw(NoServerConfigManager, - "no current server configuration manager is available"); + isc_throw(NoServerConfigManager, "no current server configuration manager is available"); } return (*cfgptr); } -bool -SrvConfigMgrFactory::initialized() { +bool SrvConfigMgrFactory::initialized() { SrvConfigMgr* cfgptr = getConfigurationMgrPtr().get(); return (cfgptr != NULL); } diff --git a/src/lib/dhcpsrv/srv_config_mgr_factory.h b/src/lib/dhcpsrv/srv_config_mgr_factory.h index 8109106747..e946f21dd0 100644 --- a/src/lib/dhcpsrv/srv_config_mgr_factory.h +++ b/src/lib/dhcpsrv/srv_config_mgr_factory.h @@ -114,4 +114,4 @@ class SrvConfigMgrFactory { } // namespace dhcp } // namespace isc -#endif // SRV_CONFIG_MGR_FACTORY_H +#endif // SRV_CONFIG_MGR_FACTORY_H From dd4327b7f0a03e9beb4df2875f3b14a3da473855 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Thu, 14 Jun 2018 20:35:14 +0300 Subject: [PATCH 15/29] save credentials on shard operations --- .../kea_config_tool/config_tool_controller.cc | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/bin/kea_config_tool/config_tool_controller.cc b/src/bin/kea_config_tool/config_tool_controller.cc index 779d94c247..25a72a5100 100644 --- a/src/bin/kea_config_tool/config_tool_controller.cc +++ b/src/bin/kea_config_tool/config_tool_controller.cc @@ -367,6 +367,9 @@ void ConfigToolController::getShardsConfig(DhcpSpaceType dhcp_space, isc_throw(RunTimeFail, "unknown configuration type"); } + std::string const tab = " "; + std::string const tabs[] = {"", tab, tab + tab, tab + tab + tab}; + for (std::string const& shard_name : filtered_shards_list) { std::string output_config_path = output_shards_directory_path; @@ -409,6 +412,23 @@ void ConfigToolController::getShardsConfig(DhcpSpaceType dhcp_space, getShardGenericConfig(dhcp_space, shard_name, credentials_config, output_config_path); getShardJsonConfig(dhcp_space, shard_name, credentials_config, output_config_path); + + std::ostringstream json_data; + json_data << "{" << std::endl + << tabs[1] << "\"config-database\": " << credentials_config << std::endl + << "}" << std::endl; + + boost::filesystem::path fileFullPath(output_config_path); + boost::filesystem::path credentialsFileFullPath = fileFullPath; + credentialsFileFullPath += "/credentials.json"; + + std::ofstream credentials_file(credentialsFileFullPath.c_str()); + // Check for permissions to write into the file + if (!credentials_file.is_open()) { + isc_throw(RunTimeFail, "cannot write the configuration file in the specified path."); + } + credentials_file << json_data.str(); + credentials_file.close(); } } From f71d3540f2a71a51615db3cc9cb193f3463ff869 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Thu, 14 Jun 2018 21:21:18 +0300 Subject: [PATCH 16/29] fixed bug when iterating through shards --- src/bin/kea_config_tool/config_tool_controller.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/bin/kea_config_tool/config_tool_controller.cc b/src/bin/kea_config_tool/config_tool_controller.cc index 25a72a5100..955fbc0308 100644 --- a/src/bin/kea_config_tool/config_tool_controller.cc +++ b/src/bin/kea_config_tool/config_tool_controller.cc @@ -213,7 +213,8 @@ void ConfigToolController::setShardsConfig(DhcpSpaceType dhcp_space, isc::data::ConstElementPtr dhcp_config = ConfigToolController::configureConfigToolConfigSource(dhcp_space, master_config_file); SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); - if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + CfgSrvConfigType::ConfigurationType data_type = srv_cfg->getConfigurationType().type_; + if (data_type == CfgSrvConfigType::MASTER_DATABASE) { if (!ConfigToolController::configureConfigToolId(dhcp_config)) { isc_throw(RunTimeFail, "config tool id not set in configuration as" " mandatory 'instance-id' parameter is missing"); @@ -228,7 +229,7 @@ void ConfigToolController::setShardsConfig(DhcpSpaceType dhcp_space, isc_throw(isc::BadValue, "invalid DHCP space type"); } cfg_db->createSrvMasterCfgManagers(); - } else if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::CONFIG_DATABASE) { + } else if (data_type == CfgSrvConfigType::CONFIG_DATABASE) { // Get 'config-database' component from the config. auto config_database = dhcp_config->get("config-database"); if (!config_database) { @@ -286,7 +287,7 @@ void ConfigToolController::setShardsConfig(DhcpSpaceType dhcp_space, std::string generic_cfg_file = shard_cfg_path + "/config.generic"; std::string timestamp_file = shard_cfg_path + "/config.timestamp"; - if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + if (data_type == CfgSrvConfigType::MASTER_DATABASE) { parseMasterCredentialsJson(shard_name, credentials_cfg_file, credentials_config); } @@ -315,7 +316,8 @@ void ConfigToolController::getShardsConfig(DhcpSpaceType dhcp_space, isc::data::ConstElementPtr dhcp_config = ConfigToolController::configureConfigToolConfigSource(dhcp_space, master_config_file); SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); - if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + CfgSrvConfigType::ConfigurationType data_type = srv_cfg->getConfigurationType().type_; + if (data_type == CfgSrvConfigType::MASTER_DATABASE) { if (!ConfigToolController::configureConfigToolId(dhcp_config)) { isc_throw(RunTimeFail, "config tool id not set in configuration as" " mandatory 'instance-id' parameter is missing"); @@ -331,7 +333,7 @@ void ConfigToolController::getShardsConfig(DhcpSpaceType dhcp_space, } cfg_db->createSrvMasterCfgManagers(); filtered_shards_list = shards_list; - } else if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::CONFIG_DATABASE) { + } else if (data_type == CfgSrvConfigType::CONFIG_DATABASE) { // Get 'config-database' component from the config. auto config_database = dhcp_config->get("config-database"); if (!config_database) { @@ -373,7 +375,7 @@ void ConfigToolController::getShardsConfig(DhcpSpaceType dhcp_space, for (std::string const& shard_name : filtered_shards_list) { std::string output_config_path = output_shards_directory_path; - if (srv_cfg->getConfigurationType().type_ == CfgSrvConfigType::MASTER_DATABASE) { + if (data_type == CfgSrvConfigType::MASTER_DATABASE) { std::string tool_id = CfgMgr::instance().getStagingCfg()->getInstanceId(); // Read the contents from database From 2fdb676ca4c3ad266b0e04b7a2bcefa16664852c Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Thu, 14 Jun 2018 22:32:09 +0300 Subject: [PATCH 17/29] reset configuration for each shard --- src/bin/kea_config_tool/config_tool_controller.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bin/kea_config_tool/config_tool_controller.cc b/src/bin/kea_config_tool/config_tool_controller.cc index 955fbc0308..02acec2079 100644 --- a/src/bin/kea_config_tool/config_tool_controller.cc +++ b/src/bin/kea_config_tool/config_tool_controller.cc @@ -565,6 +565,8 @@ void ConfigToolController::updateShardConfig(DhcpSpaceType dhcp_space, IfaceMgr::instance().setServerMode(false); + CfgMgr::instance().clear(); + isc::data::ConstElementPtr answer, dhcp_element, json; json = isc::data::Element::fromJSON(json_config, true); From b32355d4385cda0a44bda19e04d33ddd5c694482 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 15 Jun 2018 10:42:18 +0300 Subject: [PATCH 18/29] allow zero servers to be configured in master database --- .../kea_config_tool/config_tool_controller.cc | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/bin/kea_config_tool/config_tool_controller.cc b/src/bin/kea_config_tool/config_tool_controller.cc index 02acec2079..6b7cfd807c 100644 --- a/src/bin/kea_config_tool/config_tool_controller.cc +++ b/src/bin/kea_config_tool/config_tool_controller.cc @@ -991,6 +991,16 @@ void ConfigToolController::parseMasterServersJson( "to add { } around your configuration?"); } + // Get 'master-config' component from the config. + auto master_config_node = json->get("master-config"); + if (!master_config_node) { + isc_throw(isc::BadValue, "no mandatory 'master-config' entry in the configuration"); + } + + if (!master_config_node->listValue().size()) { + return; + } + // Get 'config-database' component from the config. auto config_database = json->get("config-database"); if (!config_database) { @@ -1017,12 +1027,6 @@ void ConfigToolController::parseMasterServersJson( << "' specified in `config-database`."); } - // Get 'master-config' component from the config. - auto master_config_node = json->get("master-config"); - if (!master_config_node) { - isc_throw(isc::BadValue, "no mandatory 'master-config' entry in the configuration"); - } - for (isc::data::ConstElementPtr server_config_node : master_config_node->listValue()) { ServerConfig::ServerMasterConfigPtr server_config = boost::make_shared(); @@ -1164,6 +1168,10 @@ ConfigToolController::masterSrvConfigDataToJSON(std::vector Date: Mon, 18 Jun 2018 14:43:34 +0300 Subject: [PATCH 19/29] updated create database and users --- src/bin/admin/kea-admin.in | 49 ++++++++++++-------------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index bb96b09dd8..88ae83f717 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -1023,48 +1023,29 @@ memfile_create_database_and_users() { } mysql_create_database_and_users() { - mysql_execute "GRANT ALL ON *.* TO keatest IDENTIFIED BY 'keatest';" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} - mysql_execute "CREATE DATABASE keatest;" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} - mysql_execute "GRANT SELECT ON keatest.* TO keatest_readonly IDENTIFIED BY 'keatest';" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} - exit 0 + for query in "GRANT ALL ON *.* TO keatest IDENTIFIED BY 'keatest';" \ + "CREATE DATABASE keatest;" \ + "CREATE USER 'keatest_readonly'@'localhost' IDENTIFIED BY 'keatest';" \ + "GRANT SELECT ON keatest.* TO keatest_readonly IDENTIFIED BY 'keatest';" \ + ; do + mysql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --user=root --password=${db_password} + done } pgsql_create_database_and_users() { export db_password - query="DO -\$body\$ -BEGIN - IF NOT EXISTS ( - SELECT usename - FROM pg_catalog.pg_user - WHERE usename = 'keatest') THEN - CREATE ROLE keatest LOGIN PASSWORD 'keatest'; - ALTER USER keatest CREATEDB; - END IF; -END -\$body\$;" - pgsql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --username=postgres - pgsql_execute "CREATE DATABASE keatest;" --host=${db_server_address} --port=${db_server_port} --username=postgres - pgsql_execute "GRANT ALL PRIVILEGES ON DATABASE keatest TO keatest;" --host=${db_server_address} --port=${db_server_port} --username=postgres - query="DO -\$body\$ -BEGIN - IF NOT EXISTS ( - SELECT usename - FROM pg_catalog.pg_user - WHERE usename = 'keatest_readonly') THEN - CREATE ROLE keatest_readonly LOGIN PASSWORD 'keatest'; - END IF; -END -\$body\$;" - pgsql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --username=postgres - pgsql_execute "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES to keatest_readonly;" --host=${db_server_address} --port=${db_server_port} --username=postgres - exit 0 + for query in "CREATE USER keatest WITH PASSWORD 'keatest';" \ + "CREATE DATABASE keatest;" \ + "GRANT ALL ON DATABASE keatest TO keatest;" \ + "CREATE USER keatest_readonly WITH PASSWORD 'keatest';" \ + ; do + pgsql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --username=postgres + done + pgsql_execute "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO keatest_readonly;" --host=${db_server_address} --port=${db_server_port} --username=keatest } cql_create_database_and_users() { cql_execute_no_keyspace "CREATE KEYSPACE keatest WITH replication = {'class' : 'SimpleStrategy','replication_factor' : 1}" - exit 0 } ### Script starts here ### From 366460156d933a7452cbf7e41daefa691e478ebc Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Thu, 28 Jun 2018 18:57:48 +0300 Subject: [PATCH 20/29] fixed merge --- src/lib/dhcpsrv/cfg_hosts.h | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/lib/dhcpsrv/cfg_hosts.h b/src/lib/dhcpsrv/cfg_hosts.h index 30d2359ada..2c03a543bc 100644 --- a/src/lib/dhcpsrv/cfg_hosts.h +++ b/src/lib/dhcpsrv/cfg_hosts.h @@ -50,34 +50,6 @@ class CfgHosts : public BaseHostDataSource, public WritableHostDataSource, /// @return Collection of const @c Host objects. virtual HostCollection getAll() const; - /// @brief Return all hosts for the specified HW address or DUID. - /// - /// This method returns all @c Host objects which represent reservations - /// for the specified HW address or DUID. Note, that this method may - /// return multiple reservations because a particular client may have - /// reservations in multiple subnets and the same client may be identified - /// by HW address or DUID. The server is unable to verify that the specific - /// DUID and HW address belong to the same client, until the client sends - /// a DHCP message. - /// - /// @param hwaddr HW address of the client or NULL if no HW address - /// available. - /// @param duid client id or NULL if not available, e.g. DHCPv4 client case. - /// - /// @return Collection of const @c Host objects. - virtual ConstHostCollection - getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const; - - /// @brief Non-const version of the @c getAll const method. - /// - /// @param hwaddr HW address of the client or NULL if no HW address - /// available. - /// @param duid client id or NULL if not available, e.g. DHCPv4 client case. - /// - /// @return Collection of non-const @c Host objects. - virtual HostCollection - getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()); - /// @brief Return all hosts connected to any subnet for which reservations /// have been made using a specified identifier. /// From 7cf39d06ef45666b403fda8b199df4f05af8b64a Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 12:30:57 +0300 Subject: [PATCH 21/29] fixed missing file --- src/bin/admin/kea-admin.in | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 88ae83f717..472c043b8f 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -359,14 +359,6 @@ cql_db_create() { strategy="SimpleStrategy" replication="1" - #loading the keyspace strategy and replication from file - . "$scripts_dir/cql/masterdb_create_config.sh" - ERRCODE=$? - if [ "$ERRCODE" -ne 0 ]; then - log_error "Could not execute script 'masterdb_create_config.sh' file, status code: $ERRCODE?" - exit 1 - fi - for name in $(echo $db_name | tr "," "\n") do printf "Creating '$name' keyspace\n" From b0a1e88c16ca73e60944222c273627723e5f56bb Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 13:23:44 +0300 Subject: [PATCH 22/29] fixed password for user keatest --- src/bin/admin/kea-admin.in | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 472c043b8f..df650d9845 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -1033,6 +1033,7 @@ pgsql_create_database_and_users() { ; do pgsql_execute "${query}" --host=${db_server_address} --port=${db_server_port} --username=postgres done + export db_password="keatest" pgsql_execute "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO keatest_readonly;" --host=${db_server_address} --port=${db_server_port} --username=keatest } From a9d8a402c4e1427d778fee15c50a9d2d1732ff21 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 14:22:01 +0300 Subject: [PATCH 23/29] updated initial config --- .../initial-config-mysql/kea4/kea_shard/credentials.json | 6 +++--- .../initial-config-mysql/kea4/kea_shard/servers.json | 4 ++-- .../initial-config-mysql/kea6/kea_shard/credentials.json | 6 +++--- .../initial-config-mysql/kea6/kea_shard/servers.json | 4 ++-- .../initial-config-pgsql/kea4/kea_shard/credentials.json | 6 +++--- .../initial-config-pgsql/kea4/kea_shard/servers.json | 4 ++-- .../initial-config-pgsql/kea6/kea_shard/credentials.json | 6 +++--- .../initial-config-pgsql/kea6/kea_shard/servers.json | 4 ++-- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json index de3f15205a..dd45e8c5b3 100644 --- a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/credentials.json @@ -3,7 +3,7 @@ "user": "keatest", "type": "mysql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" } -} \ No newline at end of file +} diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json index 7cd6568884..17eb815963 100644 --- a/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json @@ -3,8 +3,8 @@ "user": "keatest", "type": "mysql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" }, "master-config": [ { diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json index de3f15205a..dd45e8c5b3 100644 --- a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/credentials.json @@ -3,7 +3,7 @@ "user": "keatest", "type": "mysql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" } -} \ No newline at end of file +} diff --git a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json index 7a751beded..2ba7b233ce 100644 --- a/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json +++ b/doc/examples/kea-config-tool/initial-config-mysql/kea6/kea_shard/servers.json @@ -3,8 +3,8 @@ "user": "keatest", "type": "mysql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" }, "master-config": [ { diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json index 2e9ac9035c..8e97420584 100644 --- a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/credentials.json @@ -3,7 +3,7 @@ "user": "keatest", "type": "postgresql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" } -} \ No newline at end of file +} diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json index c70e894edc..a509b3547b 100644 --- a/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea4/kea_shard/servers.json @@ -3,8 +3,8 @@ "user": "keatest", "type": "postgresql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" }, "master-config": [ { diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json index 2e9ac9035c..8e97420584 100644 --- a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/credentials.json @@ -3,7 +3,7 @@ "user": "keatest", "type": "postgresql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" } -} \ No newline at end of file +} diff --git a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json index 63a10af585..f8eb84e6e6 100644 --- a/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json +++ b/doc/examples/kea-config-tool/initial-config-pgsql/kea6/kea_shard/servers.json @@ -3,8 +3,8 @@ "user": "keatest", "type": "postgresql", "password": "keatest", - "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "name": "kea_shard", + "host": "127.0.0.1" }, "master-config": [ { From 35621760c1ac28ded1a2f66a2c18e95afffb58e1 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 14:45:00 +0300 Subject: [PATCH 24/29] updated initial config --- .../initial-config-cql/kea4/kea_shard/credentials.json | 4 ++-- .../initial-config-cql/kea4/kea_shard/servers.json | 2 +- .../initial-config-cql/kea6/kea_shard/credentials.json | 4 ++-- .../initial-config-cql/kea6/kea_shard/servers.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json index 86b3de9994..66f1dd2e2a 100644 --- a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json +++ b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/credentials.json @@ -4,6 +4,6 @@ "type": "cql", "password": "keatest", "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "contact-points": "127.0.0.1" } -} \ No newline at end of file +} diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json index b243fda4e2..6dcfc9ebf0 100644 --- a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json +++ b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/servers.json @@ -4,7 +4,7 @@ "type": "cql", "password": "keatest", "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "contact-points": "127.0.0.1" }, "master-config": [ { diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json index 86b3de9994..66f1dd2e2a 100644 --- a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json +++ b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/credentials.json @@ -4,6 +4,6 @@ "type": "cql", "password": "keatest", "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "contact-points": "127.0.0.1" } -} \ No newline at end of file +} diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json index 0910cee47d..c3bba83394 100644 --- a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json +++ b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/servers.json @@ -4,7 +4,7 @@ "type": "cql", "password": "keatest", "keyspace": "kea_shard", - "contact-points": "192.168.128.17,192.168.128.18,192.168.128.19,192.168.128.20,192.168.128.21,192.168.128.22" + "contact-points": "127.0.0.1" }, "master-config": [ { From f0e653fe57b806f727e04d9c434a1add89d1659d Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 15:18:58 +0300 Subject: [PATCH 25/29] updated initial config --- .../initial-config-cql/kea4/kea_shard/config.json | 4 ++-- .../initial-config-cql/kea6/kea_shard/config.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json index 855842fe87..d8cdb7f9d0 100644 --- a/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json +++ b/doc/examples/kea-config-tool/initial-config-cql/kea4/kea_shard/config.json @@ -4,9 +4,9 @@ "contact-points": "127.0.0.1", "keyspace": "kea_shard", "max-statement-tries": 1, - "password": "", + "password": "keatest", "type": "cql", - "user": "" + "user": "keatest" }, "expired-leases-processing": { "flush-reclaimed-timer-wait-time": 25, diff --git a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json index 7cd183c5b0..6995411c0b 100644 --- a/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json +++ b/doc/examples/kea-config-tool/initial-config-cql/kea6/kea_shard/config.json @@ -10,9 +10,9 @@ "contact-points": "127.0.0.1", "keyspace": "kea_shard", "max-statement-tries": 1, - "password": "", + "password": "keatest", "type": "cql", - "user": "" + "user": "keatest" }, "expired-leases-processing": { "flush-reclaimed-timer-wait-time": 25, From 7dffe0555a620ee830ad79b0aff2c0502c8328bc Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 16:02:59 +0300 Subject: [PATCH 26/29] added more examples --- .../{kea4-cql.conf => kea4-master-cql.conf} | 0 ...kea4-mysql.conf => kea4-master-mysql.conf} | 0 ...kea4-pgsql.conf => kea4-master-pgsql.conf} | 0 .../local/kea4/kea4-shard-cql.conf | 13 ++++++++++ .../local/kea4/kea4-shard-mysql.conf | 12 ++++++++++ .../local/kea4/kea4-shard-pgsql.conf | 12 ++++++++++ .../{kea6-cql.conf => kea6-master-cql.conf} | 0 ...kea6-mysql.conf => kea6-master-mysql.conf} | 0 ...kea6-pgsql.conf => kea6-master-pgsql.conf} | 0 .../local/kea6/kea6-shard-cql.conf | 13 ++++++++++ .../local/kea6/kea6-shard-mysql.conf | 12 ++++++++++ .../local/kea6/kea6-shard-pgsql.conf | 12 ++++++++++ .../kea-config-tool/shard-db-connect-cql.ini | 24 +++++++++++++++++++ .../shard-db-connect-mysql.ini | 22 +++++++++++++++++ .../shard-db-connect-pgsql.ini | 22 +++++++++++++++++ 15 files changed, 142 insertions(+) rename doc/examples/kea-config-tool/local/kea4/{kea4-cql.conf => kea4-master-cql.conf} (100%) rename doc/examples/kea-config-tool/local/kea4/{kea4-mysql.conf => kea4-master-mysql.conf} (100%) rename doc/examples/kea-config-tool/local/kea4/{kea4-pgsql.conf => kea4-master-pgsql.conf} (100%) create mode 100644 doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf create mode 100644 doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf create mode 100644 doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf rename doc/examples/kea-config-tool/local/kea6/{kea6-cql.conf => kea6-master-cql.conf} (100%) rename doc/examples/kea-config-tool/local/kea6/{kea6-mysql.conf => kea6-master-mysql.conf} (100%) rename doc/examples/kea-config-tool/local/kea6/{kea6-pgsql.conf => kea6-master-pgsql.conf} (100%) create mode 100644 doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf create mode 100644 doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf create mode 100644 doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf create mode 100644 doc/examples/kea-config-tool/shard-db-connect-cql.ini create mode 100644 doc/examples/kea-config-tool/shard-db-connect-mysql.ini create mode 100644 doc/examples/kea-config-tool/shard-db-connect-pgsql.ini diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-cql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-master-cql.conf similarity index 100% rename from doc/examples/kea-config-tool/local/kea4/kea4-cql.conf rename to doc/examples/kea-config-tool/local/kea4/kea4-master-cql.conf diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-mysql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-master-mysql.conf similarity index 100% rename from doc/examples/kea-config-tool/local/kea4/kea4-mysql.conf rename to doc/examples/kea-config-tool/local/kea4/kea4-master-mysql.conf diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-pgsql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-master-pgsql.conf similarity index 100% rename from doc/examples/kea-config-tool/local/kea4/kea4-pgsql.conf rename to doc/examples/kea-config-tool/local/kea4/kea4-master-pgsql.conf diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf new file mode 100644 index 0000000000..8a30a9ac80 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf @@ -0,0 +1,13 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "config-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "keatest", + "type": "cql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf new file mode 100644 index 0000000000..351c4edae1 --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf @@ -0,0 +1,12 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf new file mode 100644 index 0000000000..8a89d1976b --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf @@ -0,0 +1,12 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-cql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-master-cql.conf similarity index 100% rename from doc/examples/kea-config-tool/local/kea6/kea6-cql.conf rename to doc/examples/kea-config-tool/local/kea6/kea6-master-cql.conf diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-mysql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-master-mysql.conf similarity index 100% rename from doc/examples/kea-config-tool/local/kea6/kea6-mysql.conf rename to doc/examples/kea-config-tool/local/kea6/kea6-master-mysql.conf diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-pgsql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-master-pgsql.conf similarity index 100% rename from doc/examples/kea-config-tool/local/kea6/kea6-pgsql.conf rename to doc/examples/kea-config-tool/local/kea6/kea6-master-pgsql.conf diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf new file mode 100644 index 0000000000..57fba831cf --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf @@ -0,0 +1,13 @@ +{ + "Dhcp6": { + "configuration-type": "database", + "config-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "keatest", + "type": "cql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf new file mode 100644 index 0000000000..5179e7a04f --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf @@ -0,0 +1,12 @@ +{ + "Dhcp6": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf new file mode 100644 index 0000000000..5cb65e45fb --- /dev/null +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf @@ -0,0 +1,12 @@ +{ + "Dhcp6": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/shard-db-connect-cql.ini b/doc/examples/kea-config-tool/shard-db-connect-cql.ini new file mode 100644 index 0000000000..d50039e81c --- /dev/null +++ b/doc/examples/kea-config-tool/shard-db-connect-cql.ini @@ -0,0 +1,24 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "config-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "", + "type": "cql", + "user": "" + } + }, + "Dhcp6": { + "configuration-type": "database", + "config-database": { + "contact-points": "127.0.0.1", + "keyspace": "kea_master", + "max-statement-tries": 1, + "password": "", + "type": "cql", + "user": "" + } + } +} diff --git a/doc/examples/kea-config-tool/shard-db-connect-mysql.ini b/doc/examples/kea-config-tool/shard-db-connect-mysql.ini new file mode 100644 index 0000000000..32e757e17b --- /dev/null +++ b/doc/examples/kea-config-tool/shard-db-connect-mysql.ini @@ -0,0 +1,22 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + }, + "Dhcp6": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "mysql", + "user": "keatest" + } + } +} diff --git a/doc/examples/kea-config-tool/shard-db-connect-pgsql.ini b/doc/examples/kea-config-tool/shard-db-connect-pgsql.ini new file mode 100644 index 0000000000..daa2cdf83e --- /dev/null +++ b/doc/examples/kea-config-tool/shard-db-connect-pgsql.ini @@ -0,0 +1,22 @@ +{ + "Dhcp4": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + }, + "Dhcp6": { + "configuration-type": "database", + "config-database": { + "host": "127.0.0.1", + "name": "kea_master", + "password": "keatest", + "type": "postgresql", + "user": "keatest" + } + } +} From 3750532a1c9840f16c9dded3ba02b6be3d750a30 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 17:45:09 +0300 Subject: [PATCH 27/29] updated configuration --- doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf | 2 +- doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf | 2 +- doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf | 2 +- doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf | 2 +- doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf | 2 +- doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf | 2 +- doc/examples/kea-config-tool/shard-db-connect-cql.ini | 4 ++-- doc/examples/kea-config-tool/shard-db-connect-mysql.ini | 4 ++-- doc/examples/kea-config-tool/shard-db-connect-pgsql.ini | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf index 8a30a9ac80..e81a7af432 100644 --- a/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "contact-points": "127.0.0.1", - "keyspace": "kea_master", + "keyspace": "kea_shard", "max-statement-tries": 1, "password": "keatest", "type": "cql", diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf index 351c4edae1..c9116ba37e 100644 --- a/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "mysql", "user": "keatest" diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf index 8a89d1976b..62c8b1130c 100644 --- a/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "postgresql", "user": "keatest" diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf index 57fba831cf..0606c1ab02 100644 --- a/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "contact-points": "127.0.0.1", - "keyspace": "kea_master", + "keyspace": "kea_shard", "max-statement-tries": 1, "password": "keatest", "type": "cql", diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf index 5179e7a04f..489ec4de4c 100644 --- a/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "mysql", "user": "keatest" diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf index 5cb65e45fb..fee2f61ca8 100644 --- a/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "postgresql", "user": "keatest" diff --git a/doc/examples/kea-config-tool/shard-db-connect-cql.ini b/doc/examples/kea-config-tool/shard-db-connect-cql.ini index d50039e81c..a2371e77fe 100644 --- a/doc/examples/kea-config-tool/shard-db-connect-cql.ini +++ b/doc/examples/kea-config-tool/shard-db-connect-cql.ini @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "contact-points": "127.0.0.1", - "keyspace": "kea_master", + "keyspace": "kea_shard", "max-statement-tries": 1, "password": "", "type": "cql", @@ -14,7 +14,7 @@ "configuration-type": "database", "config-database": { "contact-points": "127.0.0.1", - "keyspace": "kea_master", + "keyspace": "kea_shard", "max-statement-tries": 1, "password": "", "type": "cql", diff --git a/doc/examples/kea-config-tool/shard-db-connect-mysql.ini b/doc/examples/kea-config-tool/shard-db-connect-mysql.ini index 32e757e17b..22f094797d 100644 --- a/doc/examples/kea-config-tool/shard-db-connect-mysql.ini +++ b/doc/examples/kea-config-tool/shard-db-connect-mysql.ini @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "mysql", "user": "keatest" @@ -13,7 +13,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "mysql", "user": "keatest" diff --git a/doc/examples/kea-config-tool/shard-db-connect-pgsql.ini b/doc/examples/kea-config-tool/shard-db-connect-pgsql.ini index daa2cdf83e..708648825b 100644 --- a/doc/examples/kea-config-tool/shard-db-connect-pgsql.ini +++ b/doc/examples/kea-config-tool/shard-db-connect-pgsql.ini @@ -3,7 +3,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "postgresql", "user": "keatest" @@ -13,7 +13,7 @@ "configuration-type": "database", "config-database": { "host": "127.0.0.1", - "name": "kea_master", + "name": "kea_shard", "password": "keatest", "type": "postgresql", "user": "keatest" From 645014908fe63cc382e51a55b30e0dc393309165 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Fri, 29 Jun 2018 18:54:05 +0300 Subject: [PATCH 28/29] added files to makefile --- doc/Makefile.am | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index 3742f5d092..716da2c4ac 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -57,12 +57,18 @@ nobase_dist_doc_DATA += examples/kea6/simple.json nobase_dist_doc_DATA += examples/kea6/softwire46.json nobase_dist_doc_DATA += examples/kea6/stateless.json nobase_dist_doc_DATA += examples/kea6/with-ddns.json -nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-mysql.conf -nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-pgsql.conf -nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-cql.conf -nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-mysql.conf -nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-pgsql.conf -nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-cql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-master-mysql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-master-pgsql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-master-cql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea4/kea4-shard-cql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-master-mysql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-master-pgsql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-master-cql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf +nobase_dist_doc_DATA += examples/kea-config-tool/local/kea6/kea6-shard-cql.conf nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.generic nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/config.json nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-mysql/kea4/kea_shard/servers.json @@ -90,6 +96,9 @@ nobase_dist_doc_DATA += examples/kea-config-tool/initial-config-cql/kea6/kea_sha nobase_dist_doc_DATA += examples/kea-config-tool/master-db-connect-mysql.ini nobase_dist_doc_DATA += examples/kea-config-tool/master-db-connect-pgsql.ini nobase_dist_doc_DATA += examples/kea-config-tool/master-db-connect-cql.ini +nobase_dist_doc_DATA += examples/kea-config-tool/shard-db-connect-mysql.ini +nobase_dist_doc_DATA += examples/kea-config-tool/shard-db-connect-pgsql.ini +nobase_dist_doc_DATA += examples/kea-config-tool/shard-db-connect-cql.ini devel: mkdir -p html From 189ba84297f05c6a51bf9a8d105054b622745337 Mon Sep 17 00:00:00 2001 From: Razvan Becheriu Date: Mon, 23 Jul 2018 18:36:06 +0300 Subject: [PATCH 29/29] added missing configuration --- doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf | 5 +++++ .../kea-config-tool/local/kea4/kea4-shard-mysql.conf | 5 +++++ .../kea-config-tool/local/kea4/kea4-shard-pgsql.conf | 5 +++++ doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf | 5 +++++ .../kea-config-tool/local/kea6/kea6-shard-mysql.conf | 5 +++++ .../kea-config-tool/local/kea6/kea6-shard-pgsql.conf | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf index e81a7af432..bde83dd557 100644 --- a/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-cql.conf @@ -8,6 +8,11 @@ "password": "keatest", "type": "cql", "user": "keatest" + }, + "interfaces-config": { + "interfaces": [ + "ens4" + ] } } } diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf index c9116ba37e..eba53a82d8 100644 --- a/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-mysql.conf @@ -7,6 +7,11 @@ "password": "keatest", "type": "mysql", "user": "keatest" + }, + "interfaces-config": { + "interfaces": [ + "ens4" + ] } } } diff --git a/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf b/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf index 62c8b1130c..3a14417e8e 100644 --- a/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf +++ b/doc/examples/kea-config-tool/local/kea4/kea4-shard-pgsql.conf @@ -7,6 +7,11 @@ "password": "keatest", "type": "postgresql", "user": "keatest" + }, + "interfaces-config": { + "interfaces": [ + "ens4" + ] } } } diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf index 0606c1ab02..17eb29a6f8 100644 --- a/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-cql.conf @@ -8,6 +8,11 @@ "password": "keatest", "type": "cql", "user": "keatest" + }, + "interfaces-config": { + "interfaces": [ + "ens4/2001:db8:1::1" + ] } } } diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf index 489ec4de4c..fdb99ec48d 100644 --- a/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-mysql.conf @@ -7,6 +7,11 @@ "password": "keatest", "type": "mysql", "user": "keatest" + }, + "interfaces-config": { + "interfaces": [ + "ens4/2001:db8:1::1" + ] } } } diff --git a/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf b/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf index fee2f61ca8..3512d09fe7 100644 --- a/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf +++ b/doc/examples/kea-config-tool/local/kea6/kea6-shard-pgsql.conf @@ -7,6 +7,11 @@ "password": "keatest", "type": "postgresql", "user": "keatest" + }, + "interfaces-config": { + "interfaces": [ + "ens4/2001:db8:1::1" + ] } } }