From: Francis Dupont Date: Sun, 3 Dec 2017 18:23:00 +0000 (+0100) Subject: [5351] Checkpoint: todo host DB tests X-Git-Tag: trac5444_base~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f8985445646eb600d9b3c8e1dbf1bd22a68edf7b;p=thirdparty%2Fkea.git [5351] Checkpoint: todo host DB tests --- diff --git a/doc/Makefile.am b/doc/Makefile.am index 2be574a095..0210541b28 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -18,6 +18,7 @@ nobase_dist_doc_DATA += examples/kea4/advanced.json nobase_dist_doc_DATA += examples/kea4/backends.json nobase_dist_doc_DATA += examples/kea4/cassandra.json nobase_dist_doc_DATA += examples/kea4/classify.json +nobase_dist_doc_DATA += examples/kea4/comments.json nobase_dist_doc_DATA += examples/kea4/dhcpv4-over-dhcpv6.json nobase_dist_doc_DATA += examples/kea4/hooks.json nobase_dist_doc_DATA += examples/kea4/leases-expiration.json @@ -33,6 +34,7 @@ nobase_dist_doc_DATA += examples/kea6/advanced.json nobase_dist_doc_DATA += examples/kea6/backends.json nobase_dist_doc_DATA += examples/kea6/cassandra.json nobase_dist_doc_DATA += examples/kea6/classify.json +nobase_dist_doc_DATA += examples/kea6/comments.json nobase_dist_doc_DATA += examples/kea6/dhcpv4-over-dhcpv6.json nobase_dist_doc_DATA += examples/kea6/duid.json nobase_dist_doc_DATA += examples/kea6/hooks.json diff --git a/doc/examples/kea4/comments.json b/doc/examples/kea4/comments.json new file mode 100644 index 0000000000..93bfa77d6e --- /dev/null +++ b/doc/examples/kea4/comments.json @@ -0,0 +1,123 @@ +// This is an example configuration file for the DHCPv4 server in Kea. +// It uses embedded (i.e., which will be included in configuration objets +// and not stripped by at lexical analysis) comments. + +{ "Dhcp4": + +{ + // Global scope + "comment": "A DHCPv4 server", + + // In interface config + "interfaces-config": { + "comment": "Use wildcard", + "interfaces": [ "*" ] }, + + // In option definitions + "option-def": [ { + "comment": "An option definition", + "name": "foo", + "code": 100, + "type": "ipv4-address", + "space": "isc" + } ], + + // In option data + "option-data": [ { + "comment": "Set option value", + "name": "dhcp-message", + "data": "ABCDEF0105", + "csv-format": false + } ], + + // In client classes + "client-classes": [ + { + "comment": "match all", + "name": "all", + "test": "'' == ''" + }, + // Of course comments are optional + { + "name": "none" + }, + // Two comments (or user contexts) in the same scope are combined + { + "comment": "first comment", + "comment": "second comment", + "name": "two" + }, + // Of course this applies to a comment and a user context too + { + "comment": "a comment", + "name": "both", + "user-context": { + "version": 1 + } + } + ], + + // In control socket (more for the agent) + "control-socket": { + "comment": "REST API", + "socket-type": "unix", + "socket-name": "/tmp/kea4-ctrl-socket", + "user-context": { "comment": "Indirect comment" } + }, + + // In shared networks + "shared-networks": [ { + "comment": "A shared network", + "name": "foo", + + // In subnets + "subnet4": [ + { + "comment": "A subnet", + "subnet": "192.0.1.0/24", + "id": 100, + + // In pools + "pools": [ + { + "comment": "A pool", + "pool": "192.0.1.1-192.0.1.10" + } + ], + + // In host reservations + "reservations": [ + { + "comment": "A host reservation", + "hw-address": "AA:BB:CC:DD:EE:FF", + "hostname": "foo.example.com", + + // Again in an option data + "option-data": [ { + "comment": "An option in a reservation", + "name": "domain-name", + "data": "example.com" + } ] + } + ] + } + ] + } ], + + // In dhcp ddns + "dhcp-ddns": { + "comment": "No dynamic DNS", + "enable-updates": false + } +}, + +"Logging": { + // In loggers + "loggers": [ { + "comment": "A logger", + "name": "kea-dhcp4" + } ] +} + +} + diff --git a/doc/examples/kea6/comments.json b/doc/examples/kea6/comments.json new file mode 100644 index 0000000000..bd10aed258 --- /dev/null +++ b/doc/examples/kea6/comments.json @@ -0,0 +1,133 @@ +// This is an example configuration file for the DHCPv6 server in Kea. +// It uses embedded (i.e., which will be included in configuration objets +// and not stripped by at lexical analysis) comments. + +{ "Dhcp6": + +{ + // Global scope + "comment": "A DHCPv6 server", + + // In interface config + "interfaces-config": { + "comment": "Use wildcard", + "interfaces": [ "*" ] }, + + // In option definitions + "option-def": [ { + "comment": "An option definition", + "name": "foo", + "code": 100, + "type": "ipv6-address", + "space": "isc" + } ], + + // In option data + "option-data": [ { + "comment": "Set option value", + "name": "subscriber-id", + "data": "ABCDEF0105", + "csv-format": false + } ], + + // In client classes + "client-classes": [ + { + "comment": "match all", + "name": "all", + "test": "'' == ''" + }, + // Of course comments are optional + { + "name": "none" + }, + // Two comments (or user contexts) in the same scope are combined + { + "comment": "first comment", + "comment": "second comment", + "name": "two" + }, + // Of course this applies to a comment and a user context too + { + "comment": "a comment", + "name": "both", + "user-context": { + "version": 1 + } + } + ], + + // In control socket (more for the agent) + "control-socket": { + "comment": "REST API", + "socket-type": "unix", + "socket-name": "/tmp/kea6-ctrl-socket", + "user-context": { "comment": "Indirect comment" } + }, + + // In shared networks + "shared-networks": [ { + "comment": "A shared network", + "name": "foo", + + // In subnets + "subnet6": [ + { + "comment": "A subnet", + "subnet": "2001:db1::/64", + "id": 100, + + // In pools + "pools": [ + { + "comment": "A pool", + "pool": "2001:db1::/64" + } + ], + + // In prefix pools + "pd-pools": [ + { + "comment": "A prefix pool", + "prefix": "2001:db2::", + "prefix-len": 48, + "delegated-len": 64 + } + ], + + // In host reservations + "reservations": [ + { + "comment": "A host reservation", + "hw-address": "AA:BB:CC:DD:EE:FF", + "hostname": "foo.example.com", + + // Again in an option data + "option-data": [ { + "comment": "An option in a reservation", + "name": "domain-search", + "data": "example.com" + } ] + } + ] + } + ] + } ], + + // In dhcp ddns + "dhcp-ddns": { + "comment": "No dynamic DNS", + "enable-updates": false + } +}, + +"Logging": { + // In loggers + "loggers": [ { + "comment": "A logger", + "name": "kea-dhcp6" + } ] +} + +} + diff --git a/doc/guide/hooks.xml b/doc/guide/hooks.xml index 3e245beac2..e26829017a 100644 --- a/doc/guide/hooks.xml +++ b/doc/guide/hooks.xml @@ -80,7 +80,9 @@ hooks-libraries was a list of strings, each string being the name of a library. The change was made in Kea 1.0 to facilitate the specification of library-specific parameters, a capability - available in Kea 1.1.0 onwards. + available in Kea 1.1.0 onwards. Libraries should allow a parameter + entry where to put documentation as it is done for many configuration + scopes with comment and user context. @@ -2568,11 +2570,11 @@ both the command and the response. As of Kea 1.3, the structures that allow user contexts are pools of all types (addresses and prefixes) and subnets. Kea - 1.4 extended to the global scope, shared-networks, client - classes, and option data and definitions. These are supported - in both DHCPv4 and DHCPv6. It is expected that host - reservations and other structures will be extended in the - future to provide the user context capability. + 1.4 extended to the global scope, interfaces config, + shared networks, subnets, client classes, option datas and definitions, + host reservations, control socket, dhcp ddns, loggers and server id. + These are supported in both DHCPv4 and DHCPv6 at the exception + of server id which is DHCPv6 only. diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc index 40e7264caf..19ec5bc379 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -6522,6 +6522,28 @@ const char* UNPARSED_CONFIGS[] = { " },\n" " \"renew-timer\": 900,\n" " \"reservation-mode\": \"all\",\n" +" \"reservations\": [\n" +" {\n" +" \"comment\": \"A host reservation\",\n" +" \"boot-file-name\": \"\",\n" +" \"client-classes\": [ ],\n" +" \"hostname\": \"foo.example.com\",\n" +" \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"comment\": \"An option in a reservation\",\n" +" \"always-send\": false,\n" +" \"code\": 15,\n" +" \"csv-format\": true,\n" +" \"data\": \"example.com\",\n" +" \"name\": \"domain-name\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"server-hostname\": \"\"\n" +" }\n" +" ],\n" " \"server-hostname\": \"\",\n" " \"subnet\": \"192.0.1.0/24\",\n" " \"valid-lifetime\": 7200\n" diff --git a/src/bin/dhcp4/tests/parser_unittest.cc b/src/bin/dhcp4/tests/parser_unittest.cc index d35faef2f7..7cc005ea9f 100644 --- a/src/bin/dhcp4/tests/parser_unittest.cc +++ b/src/bin/dhcp4/tests/parser_unittest.cc @@ -247,6 +247,7 @@ TEST(ParserTest, file) { "backends.json", "cassandra.json", "classify.json", + // "comments.json", "dhcpv4-over-dhcpv6.json", "hooks.json", "leases-expiration.json", diff --git a/src/bin/dhcp6/tests/get_config_unittest.cc b/src/bin/dhcp6/tests/get_config_unittest.cc index 298e71647d..959fbe96cc 100644 --- a/src/bin/dhcp6/tests/get_config_unittest.cc +++ b/src/bin/dhcp6/tests/get_config_unittest.cc @@ -6132,6 +6132,27 @@ const char* UNPARSED_CONFIGS[] = { " },\n" " \"renew-timer\": 900,\n" " \"reservation-mode\": \"all\",\n" +" \"reservations\": [\n" +" {\n" +" \"comment\": \"A host reservation\",\n" +" \"client-classes\": [ ],\n" +" \"hostname\": \"foo.example.com\",\n" +" \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n" +" \"ip-addresses\": [ ],\n" +" \"option-data\": [\n" +" {\n" +" \"comment\": \"An option in a reservation\",\n" +" \"always-send\": false,\n" +" \"code\": 24,\n" +" \"csv-format\": true,\n" +" \"data\": \"example.com\",\n" +" \"name\": \"domain-search\",\n" +" \"space\": \"dhcp6\"\n" +" }\n" +" ],\n" +" \"prefixes\": [ ]\n" +" }\n" +" ],\n" " \"subnet\": \"2001:db1::/48\",\n" " \"valid-lifetime\": 7200\n" " }\n" diff --git a/src/bin/dhcp6/tests/parser_unittest.cc b/src/bin/dhcp6/tests/parser_unittest.cc index bbc969e9e0..c2286bd9ab 100644 --- a/src/bin/dhcp6/tests/parser_unittest.cc +++ b/src/bin/dhcp6/tests/parser_unittest.cc @@ -251,6 +251,7 @@ TEST(ParserTest, file) { configs.push_back("backends.json"); configs.push_back("cassandra.json"); configs.push_back("classify.json"); + // configs.push_back("comments.json"); configs.push_back("dhcpv4-over-dhcpv6.json"); configs.push_back("duid.json"); configs.push_back("hooks.json"); diff --git a/src/lib/dhcpsrv/host.cc b/src/lib/dhcpsrv/host.cc index 4b644977cd..2f37017064 100644 --- a/src/lib/dhcpsrv/host.cc +++ b/src/lib/dhcpsrv/host.cc @@ -436,7 +436,9 @@ Host::toElement4() const { } // Set the reservation const IOAddress& address = getIPv4Reservation(); - map->set("ip-address", Element::create(address.toText())); + if (!address.isV4Zero()) { + map->set("ip-address", Element::create(address.toText())); + } // Set the hostname const std::string& hostname = getHostname(); map->set("hostname", Element::create(hostname)); diff --git a/src/lib/dhcpsrv/mysql_host_data_source.cc b/src/lib/dhcpsrv/mysql_host_data_source.cc index dbac205fe2..8f38e43a28 100644 --- a/src/lib/dhcpsrv/mysql_host_data_source.cc +++ b/src/lib/dhcpsrv/mysql_host_data_source.cc @@ -111,7 +111,7 @@ class MySqlHostExchange { private: /// @brief Number of columns returned for SELECT queries send by this class. - static const size_t HOST_COLUMNS = 12; + static const size_t HOST_COLUMNS = 13; public: @@ -129,6 +129,7 @@ public: dhcp4_subnet_id_(0), dhcp6_subnet_id_(0), ipv4_address_(0), hostname_length_(0), dhcp4_client_classes_length_(0), dhcp6_client_classes_length_(0), + user_context_length_(0), dhcp4_next_server_(0), dhcp4_server_hostname_length_(0), dhcp4_boot_file_name_length_(0), @@ -137,6 +138,7 @@ public: ipv4_address_null_(MLM_FALSE), hostname_null_(MLM_FALSE), dhcp4_client_classes_null_(MLM_FALSE), dhcp6_client_classes_null_(MLM_FALSE), + user_context_null_(MLM_FALSE), dhcp4_next_server_null_(MLM_FALSE), dhcp4_server_hostname_null_(MLM_FALSE), dhcp4_boot_file_name_null_(MLM_FALSE) { @@ -146,6 +148,7 @@ public: memset(hostname_, 0, sizeof(hostname_)); memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_)); memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_)); + memset(user_context_, 0, sizeof(user_context_)); memset(dhcp4_server_hostname_, 0, sizeof(dhcp4_server_hostname_)); memset(dhcp4_boot_file_name_, 0, sizeof(dhcp4_boot_file_name_)); @@ -161,11 +164,12 @@ public: columns_[6] = "hostname"; columns_[7] = "dhcp4_client_classes"; columns_[8] = "dhcp6_client_classes"; - columns_[9] = "dhcp4_next_server"; - columns_[10] = "dhcp4_server_hostname"; - columns_[11] = "dhcp4_boot_file_name"; + columns_[9] = "user_context"; + columns_[10] = "dhcp4_next_server"; + columns_[11] = "dhcp4_server_hostname"; + columns_[12] = "dhcp4_boot_file_name"; - BOOST_STATIC_ASSERT(11 < HOST_COLUMNS); + BOOST_STATIC_ASSERT(12 < HOST_COLUMNS); }; /// @brief Virtual destructor. @@ -352,31 +356,43 @@ public: bind_[8].buffer = dhcp6_client_classes_; bind_[8].buffer_length = classes6_txt.length(); + // user_context : TEXT NULL + ConstElementPtr ctx = host->getContext(); + if (ctx) { + bind_[9].buffer_type = MYSQL_TYPE_STRING; + string ctx_txt = ctx->str(); + strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1); + bind_[9].buffer = user_context_; + bind_[9].buffer_length = ctx_txt.length(); + } else { + bind_[9].buffer_type = MYSQL_TYPE_NULL; + } + // ipv4_address : INT UNSIGNED NULL // The address in the Host structure is an IOAddress object. Convert // this to an integer for storage. dhcp4_next_server_ = host->getNextServer().toUint32(); - bind_[9].buffer_type = MYSQL_TYPE_LONG; - bind_[9].buffer = reinterpret_cast(&dhcp4_next_server_); - bind_[9].is_unsigned = MLM_TRUE; - // bind_[9].is_null = &MLM_FALSE; // commented out for performance - // reasons, see memset() above + bind_[10].buffer_type = MYSQL_TYPE_LONG; + bind_[10].buffer = reinterpret_cast(&dhcp4_next_server_); + bind_[10].is_unsigned = MLM_TRUE; + // bind_[10].is_null = &MLM_FALSE; // commented out for performance + // reasons, see memset() above // dhcp4_server_hostname - bind_[10].buffer_type = MYSQL_TYPE_STRING; + bind_[11].buffer_type = MYSQL_TYPE_STRING; std::string server_hostname = host->getServerHostname(); strncpy(dhcp4_server_hostname_, server_hostname.c_str(), SERVER_HOSTNAME_MAX_LEN - 1); - bind_[10].buffer = dhcp4_server_hostname_; - bind_[10].buffer_length = server_hostname.length(); + bind_[11].buffer = dhcp4_server_hostname_; + bind_[11].buffer_length = server_hostname.length(); // dhcp4_boot_file_name - bind_[11].buffer_type = MYSQL_TYPE_STRING; + bind_[12].buffer_type = MYSQL_TYPE_STRING; std::string boot_file_name = host->getBootFileName(); strncpy(dhcp4_boot_file_name_, boot_file_name.c_str(), BOOT_FILE_NAME_MAX_LEN - 1); - bind_[11].buffer = dhcp4_boot_file_name_; - bind_[11].buffer_length = boot_file_name.length(); + bind_[12].buffer = dhcp4_boot_file_name_; + bind_[12].buffer_length = boot_file_name.length(); } catch (const std::exception& ex) { isc_throw(DbOperationError, @@ -470,30 +486,39 @@ public: bind_[8].length = &dhcp6_client_classes_length_; bind_[8].is_null = &dhcp6_client_classes_null_; + // user_context : TEXT NULL + user_context_null_ = MLM_FALSE; + user_context_length_ = sizeof(user_context_); + bind_[9].buffer_type = MYSQL_TYPE_STRING; + bind_[9].buffer = reinterpret_cast(user_context_); + bind_[9].buffer_length = user_context_length_; + bind_[9].length = &user_context_length_; + bind_[9].is_null = &user_context_null_; + // dhcp4_next_server dhcp4_next_server_null_ = MLM_FALSE; - bind_[9].buffer_type = MYSQL_TYPE_LONG; - bind_[9].buffer = reinterpret_cast(&dhcp4_next_server_); - bind_[9].is_unsigned = MLM_TRUE; - bind_[9].is_null = &dhcp4_next_server_null_; + bind_[10].buffer_type = MYSQL_TYPE_LONG; + bind_[10].buffer = reinterpret_cast(&dhcp4_next_server_); + bind_[10].is_unsigned = MLM_TRUE; + bind_[10].is_null = &dhcp4_next_server_null_; // dhcp4_server_hostname dhcp4_server_hostname_null_ = MLM_FALSE; dhcp4_server_hostname_length_ = sizeof(dhcp4_server_hostname_); - bind_[10].buffer_type = MYSQL_TYPE_STRING; - bind_[10].buffer = reinterpret_cast(dhcp4_server_hostname_); - bind_[10].buffer_length = dhcp4_server_hostname_length_; - bind_[10].length = &dhcp4_server_hostname_length_; - bind_[10].is_null = &dhcp4_server_hostname_null_; + bind_[11].buffer_type = MYSQL_TYPE_STRING; + bind_[11].buffer = reinterpret_cast(dhcp4_server_hostname_); + bind_[11].buffer_length = dhcp4_server_hostname_length_; + bind_[11].length = &dhcp4_server_hostname_length_; + bind_[11].is_null = &dhcp4_server_hostname_null_; // dhcp4_boot_file_name dhcp4_boot_file_name_null_ = MLM_FALSE; dhcp4_boot_file_name_length_ = sizeof(dhcp4_boot_file_name_); - bind_[11].buffer_type = MYSQL_TYPE_STRING; - bind_[11].buffer = reinterpret_cast(dhcp4_boot_file_name_); - bind_[11].buffer_length = dhcp4_boot_file_name_length_; - bind_[11].length = &dhcp4_boot_file_name_length_; - bind_[11].is_null = &dhcp4_boot_file_name_null_; + bind_[12].buffer_type = MYSQL_TYPE_STRING; + bind_[12].buffer = reinterpret_cast(dhcp4_boot_file_name_); + bind_[12].buffer_length = dhcp4_boot_file_name_length_; + bind_[12].length = &dhcp4_boot_file_name_length_; + bind_[12].is_null = &dhcp4_boot_file_name_null_; // Add the error flags setErrorIndicators(bind_, error_); @@ -564,6 +589,13 @@ public: dhcp6_client_classes_length_); } + // Convert user_context to string as well. + std::string user_context; + if (user_context_null_ == MLM_FALSE) { + user_context_[user_context_length_] = '\0'; + user_context.assign(user_context_); + } + // Set next server value (siaddr) if non NULL value returned. asiolink::IOAddress next_server = asiolink::IOAddress::IPV4_ZERO_ADDRESS(); if (dhcp4_next_server_null_ == MLM_FALSE) { @@ -592,6 +624,21 @@ public: dhcp4_boot_file_name)); h->setHostId(host_id_); + // Set the user context if there is one. + if (!user_context.empty()) { + try { + ConstElementPtr ctx = Element::fromJSON(user_context); + if (!ctx || (ctx->getType() != Element::map)) { + isc_throw(BadValue, "user context '" << user_context + << "' is no a JSON map"); + } + h->setContext(ctx); + } catch (const isc::data::JSONError& ex) { + isc_throw(BadValue, "user context '" << user_context + << "' is invalid JSON: " << ex.what()); + } + } + return (h); }; @@ -698,6 +745,12 @@ private: /// client classes. unsigned long dhcp6_client_classes_length_; + /// @brief Buffer holding textual user context. + char user_context_[USER_CONTEXT_MAX_LEN]; + + /// @brief User context length. + unsigned long user_context_length_; + /// Next server address (siaddr). uint32_t dhcp4_next_server_; @@ -736,6 +789,9 @@ private: /// NULL. my_bool dhcp6_client_classes_null_; + /// @brief Boolean flag indicating if the value of user context is NULL. + my_bool user_context_null_; + /// Boolean flag indicating if the value of next server is NULL. my_bool dhcp4_next_server_null_; @@ -2039,6 +2095,7 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, 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, " "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, " "o4.persistent, o4.user_context, " @@ -2062,7 +2119,7 @@ TaggedStatementArray tagged_statements = { { {MySqlHostDataSourceImpl::GET_HOST_ADDR, "SELECT h.host_id, h.dhcp_identifier, 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.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 " @@ -2078,7 +2135,7 @@ TaggedStatementArray tagged_statements = { { {MySqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID, "SELECT h.host_id, h.dhcp_identifier, 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.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 " @@ -2096,7 +2153,7 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, " "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.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, " @@ -2118,7 +2175,7 @@ TaggedStatementArray tagged_statements = { { {MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, "SELECT h.host_id, h.dhcp_identifier, 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.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 " @@ -2138,7 +2195,7 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, " "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.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," @@ -2164,7 +2221,8 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, " "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.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, " @@ -2186,9 +2244,10 @@ TaggedStatementArray tagged_statements = { { {MySqlHostDataSourceImpl::INSERT_HOST, "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, " "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, " - "dhcp4_client_classes, dhcp6_client_classes, dhcp4_next_server, " + "dhcp4_client_classes, dhcp6_client_classes, " + "user_context, dhcp4_next_server, " "dhcp4_server_hostname, dhcp4_boot_file_name) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"}, + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"}, // Inserts a single IPv6 reservation into 'reservations' table. {MySqlHostDataSourceImpl::INSERT_V6_RESRV, diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc index 580f03dd2a..e955fbd5b9 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.cc +++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc @@ -91,11 +91,12 @@ private: static const int HOSTNAME_COL = 6; static const int DHCP4_CLIENT_CLASSES_COL = 7; static const int DHCP6_CLIENT_CLASSES_COL = 8; - static const int DHCP4_NEXT_SERVER_COL = 9; - static const int DHCP4_SERVER_HOSTNAME_COL = 10; - static const int DHCP4_BOOT_FILE_NAME_COL = 11; + static const int USER_CONTEXT_COL = 9; + static const int DHCP4_NEXT_SERVER_COL = 10; + static const int DHCP4_SERVER_HOSTNAME_COL = 11; + static const int DHCP4_BOOT_FILE_NAME_COL = 12; /// @brief Number of columns returned for SELECT queries send by this class. - static const size_t HOST_COLUMNS = 12; + static const size_t HOST_COLUMNS = 13; public: @@ -120,11 +121,12 @@ public: columns_[HOSTNAME_COL] = "hostname"; columns_[DHCP4_CLIENT_CLASSES_COL] = "dhcp4_client_classes"; columns_[DHCP6_CLIENT_CLASSES_COL] = "dhcp6_client_classes"; + columns_[USER_CONTEXT_COL] = "user_context"; columns_[DHCP4_NEXT_SERVER_COL] = "dhcp4_next_server"; columns_[DHCP4_SERVER_HOSTNAME_COL] = "dhcp4_server_hostname"; columns_[DHCP4_BOOT_FILE_NAME_COL] = "dhcp4_boot_file_name"; - BOOST_STATIC_ASSERT(11 < HOST_COLUMNS); + BOOST_STATIC_ASSERT(12 < HOST_COLUMNS); }; /// @brief Virtual destructor. @@ -222,6 +224,15 @@ public: // dhcp6_client_classes : VARCHAR(255) NULL bind_array->addTempString(host->getClientClasses6().toText(",")); + // user_context: TEXT NULL + ConstElementPtr ctx = host->getContext(); + if (ctx) { + std::string user_context_ = ctx->str(); + bind_array->addTempString(user_context_); + } else { + bind_array->addNull(); + } + // dhcp4_next_server : BIGINT NULL bind_array->add((host->getNextServer())); @@ -343,6 +354,12 @@ public: getColumnValue(r, row, DHCP6_CLIENT_CLASSES_COL, dhcp6_client_classes); } + // user_context: TEXT + std::string user_context; + if (!isColumnNull(r, row, USER_CONTEXT_COL)) { + getColumnValue(r, row, USER_CONTEXT_COL, user_context); + } + // dhcp4_next_server : BIGINT NULL uint32_t dhcp4_next_server_as_uint32(0); if (!isColumnNull(r, row, DHCP4_NEXT_SERVER_COL)) { @@ -372,6 +389,21 @@ public: dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name)); + // Set the user context if there is one. + if (!user_context.empty()) { + try { + ConstElementPtr ctx = Element::fromJSON(user_context); + if (!ctx || (ctx->getType() != Element::map)) { + isc_throw(BadValue, "user context '" << user_context + << "' is no a JSON map"); + } + host->setContext(ctx); + } catch (const isc::data::JSONError& ex) { + isc_throw(BadValue, "user context '" << user_context + << "' is invalid JSON: " << ex.what()); + } + } + host->setHostId(host_id); } catch (const isc::Exception& ex) { isc_throw(DbOperationError, "Could not create host: " << ex.what()); @@ -1427,6 +1459,7 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, 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, " " o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, " " o4.persistent, o4.user_context, " @@ -1449,7 +1482,7 @@ TaggedStatementArray tagged_statements = { { { OID_INT8 }, "get_host_addr", "SELECT h.host_id, h.dhcp_identifier, 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.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 " @@ -1468,7 +1501,7 @@ TaggedStatementArray tagged_statements = { { "get_host_subid4_dhcpid", "SELECT h.host_id, h.dhcp_identifier, 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.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 " @@ -1489,7 +1522,7 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, " " 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.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, " @@ -1512,7 +1545,7 @@ TaggedStatementArray tagged_statements = { { "get_host_subid_addr", "SELECT h.host_id, h.dhcp_identifier, 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.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 " @@ -1535,7 +1568,7 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, " " 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.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, " @@ -1563,7 +1596,7 @@ TaggedStatementArray tagged_statements = { { "SELECT h.host_id, h.dhcp_identifier, " " 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.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, " @@ -1586,16 +1619,17 @@ TaggedStatementArray tagged_statements = { { // PgSqlHostDataSourceImpl::INSERT_HOST // Inserts a host into the 'hosts' table. Returns the inserted host id. - {11, + {12, { OID_BYTEA, OID_INT2, OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR, - OID_VARCHAR, OID_VARCHAR }, + OID_VARCHAR, OID_VARCHAR, OID_TEXT }, "insert_host", "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, " " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, " - " dhcp4_client_classes, dhcp6_client_classes, " + " dhcp4_client_classes, dhcp6_client_classes, user_context, " " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name) " - "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING host_id" + "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) " + "RETURNING host_id" }, //PgSqlHostDataSourceImpl::INSERT_V6_RESRV diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index da3d1e2ef9..b332cbb8d4 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -242,20 +242,29 @@ SrvConfig::toElement() const { const Subnet4Collection* subnets = cfg_subnets4_->getAll(); for (Subnet4Collection::const_iterator subnet = subnets->cbegin(); subnet != subnets->cend(); ++subnet) { - ElementPtr subnet_cfg = (*subnet)->toElement(); - sn_list.push_back(subnet_cfg); - // Skip subnets which are in a shared-network SharedNetwork4Ptr network; (*subnet)->getSharedNetwork(network); if (network) { continue; } + ElementPtr subnet_cfg = (*subnet)->toElement(); + sn_list.push_back(subnet_cfg); plain_subnets->add(subnet_cfg); } dhcp->set("subnet4", plain_subnets); - ConstElementPtr shared_networks = cfg_shared_networks4_->toElement(); + ElementPtr shared_networks = cfg_shared_networks4_->toElement(); + const std::vector networks = shared_networks->listValue(); + for (auto network = networks.cbegin(); + network != networks.cend(); ++network) { + const std::vector sh_list = + (*network)->get("subnet4")->listValue(); + for (auto subnet = sh_list.cbegin(); + subnet != sh_list.cend(); ++subnet) { + sn_list.push_back(*subnet); + } + } dhcp->set("shared-networks", shared_networks); } else { @@ -263,20 +272,29 @@ SrvConfig::toElement() const { const Subnet6Collection* subnets = cfg_subnets6_->getAll(); for (Subnet6Collection::const_iterator subnet = subnets->cbegin(); subnet != subnets->cend(); ++subnet) { - ElementPtr subnet_cfg = (*subnet)->toElement(); - sn_list.push_back(subnet_cfg); - // Skip subnets which are in a shared-network SharedNetwork6Ptr network; (*subnet)->getSharedNetwork(network); if (network) { continue; } + ElementPtr subnet_cfg = (*subnet)->toElement(); + sn_list.push_back(subnet_cfg); plain_subnets->add(subnet_cfg); } dhcp->set("subnet6", plain_subnets); - ConstElementPtr shared_networks = cfg_shared_networks6_->toElement(); + ElementPtr shared_networks = cfg_shared_networks6_->toElement(); + const std::vector networks = shared_networks->listValue(); + for (auto network = networks.cbegin(); + network != networks.cend(); ++network) { + const std::vector sh_list = + (*network)->get("subnet6")->listValue(); + for (auto subnet = sh_list.cbegin(); + subnet != sh_list.cend(); ++subnet) { + sn_list.push_back(*subnet); + } + } dhcp->set("shared-networks", shared_networks); } // Insert reservations diff --git a/src/lib/dhcpsrv/tests/host_unittest.cc b/src/lib/dhcpsrv/tests/host_unittest.cc index e5e219e142..bc17bb13f4 100644 --- a/src/lib/dhcpsrv/tests/host_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_unittest.cc @@ -1091,7 +1091,6 @@ TEST_F(HostTest, unparse) { "\"comment\": \"a host reservation\", " "\"hostname\": \"\", " "\"hw-address\": \"01:02:03:04:05:06\", " - "\"ip-address\": \"0.0.0.0\", " "\"next-server\": \"0.0.0.0\", " "\"option-data\": [ ], " "\"server-hostname\": \"\" " @@ -1121,7 +1120,6 @@ TEST_F(HostTest, unparse) { "\"client-classes\": [ ], " "\"duid\": \"11:12:13:14:15\", " "\"hostname\": \"myhost\", " - "\"ip-address\": \"0.0.0.0\", " "\"next-server\": \"0.0.0.0\", " "\"option-data\": [ ], " "\"server-hostname\": \"\" " @@ -1147,7 +1145,6 @@ TEST_F(HostTest, unparse) { "\"client-classes\": [ \"modem\", \"router\" ], " "\"duid\": \"11:12:13:14:15\", " "\"hostname\": \"myhost\", " - "\"ip-address\": \"0.0.0.0\", " "\"next-server\": \"0.0.0.0\", " "\"option-data\": [ ], " "\"server-hostname\": \"\" " @@ -1172,7 +1169,6 @@ TEST_F(HostTest, unparse) { "\"client-classes\": [ \"modem\", \"router\" ], " "\"duid\": \"11:12:13:14:15\", " "\"hostname\": \"myhost\", " - "\"ip-address\": \"0.0.0.0\", " "\"next-server\": \"0.0.0.0\", " "\"option-data\": [ ], " "\"server-hostname\": \"\" " diff --git a/src/share/database/scripts/mysql/dhcpdb_create.mysql b/src/share/database/scripts/mysql/dhcpdb_create.mysql index 3259648ea7..dc512f7314 100644 --- a/src/share/database/scripts/mysql/dhcpdb_create.mysql +++ b/src/share/database/scripts/mysql/dhcpdb_create.mysql @@ -498,6 +498,9 @@ END $$ DELIMITER ; +# Add user context into table holding hosts +ALTER TABLE hosts ADD COLUMN user_context TEXT NULL; + # Add user contexts into tables holding DHCP options ALTER TABLE dhcp4_options ADD COLUMN user_context TEXT NULL; ALTER TABLE dhcp6_options ADD COLUMN user_context TEXT NULL; diff --git a/src/share/database/scripts/mysql/upgrade_5.0_to_5.1.sh.in b/src/share/database/scripts/mysql/upgrade_5.0_to_5.1.sh.in index 54f8109250..135c08fc24 100644 --- a/src/share/database/scripts/mysql/upgrade_5.0_to_5.1.sh.in +++ b/src/share/database/scripts/mysql/upgrade_5.0_to_5.1.sh.in @@ -35,6 +35,9 @@ END $$ DELIMITER ; +# Add user context into table holding hosts +ALTER TABLE hosts ADD COLUMN user_context TEXT NULL; + # Add user contexts into tables holding DHCP options ALTER TABLE dhcp4_options ADD COLUMN user_context TEXT NULL; ALTER TABLE dhcp6_options ADD COLUMN user_context TEXT NULL; diff --git a/src/share/database/scripts/pgsql/dhcpdb_create.pgsql b/src/share/database/scripts/pgsql/dhcpdb_create.pgsql index 51a50616f9..b73eeef8b3 100644 --- a/src/share/database/scripts/pgsql/dhcpdb_create.pgsql +++ b/src/share/database/scripts/pgsql/dhcpdb_create.pgsql @@ -524,6 +524,9 @@ CREATE UNIQUE INDEX key_dhcp6_identifier_subnet_id ON hosts (dhcp_identifier ASC, dhcp_identifier_type ASC, dhcp6_subnet_id ASC) WHERE (dhcp6_subnet_id IS NOT NULL AND dhcp6_subnet_id <> 0); +-- Add a column holding hosts for user context. +ALTER TABLE hosts ADD COLUMN user_context TEXT; + -- Add a column holding DHCP options for user context. ALTER TABLE dhcp4_options ADD COLUMN user_context TEXT; ALTER TABLE dhcp6_options ADD COLUMN user_context TEXT; diff --git a/src/share/database/scripts/pgsql/upgrade_3.1_to_3.2.sh.in b/src/share/database/scripts/pgsql/upgrade_3.1_to_3.2.sh.in index 78e212ce5c..ca1e4d7b8b 100644 --- a/src/share/database/scripts/pgsql/upgrade_3.1_to_3.2.sh.in +++ b/src/share/database/scripts/pgsql/upgrade_3.1_to_3.2.sh.in @@ -48,6 +48,9 @@ CREATE UNIQUE INDEX hosts_dhcp6_identifier_subnet_id ON hosts (dhcp_identifier ASC, dhcp_identifier_type ASC, dhcp6_subnet_id ASC) WHERE (dhcp6_subnet_id IS NOT NULL AND dhcp6_subnet_id <> 0); +-- Add a column holding hosts for user context. +ALTER TABLE hosts ADD COLUMN user_context TEXT; + -- Add a column holding DHCP options for user context. ALTER TABLE dhcp4_options ADD COLUMN user_context TEXT; ALTER TABLE dhcp6_options ADD COLUMN user_context TEXT;