]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5351] Checkpoint: todo host DB tests
authorFrancis Dupont <fdupont@isc.org>
Sun, 3 Dec 2017 18:23:00 +0000 (19:23 +0100)
committerFrancis Dupont <fdupont@isc.org>
Sun, 3 Dec 2017 18:23:00 +0000 (19:23 +0100)
17 files changed:
doc/Makefile.am
doc/examples/kea4/comments.json [new file with mode: 0644]
doc/examples/kea6/comments.json [new file with mode: 0644]
doc/guide/hooks.xml
src/bin/dhcp4/tests/get_config_unittest.cc
src/bin/dhcp4/tests/parser_unittest.cc
src/bin/dhcp6/tests/get_config_unittest.cc
src/bin/dhcp6/tests/parser_unittest.cc
src/lib/dhcpsrv/host.cc
src/lib/dhcpsrv/mysql_host_data_source.cc
src/lib/dhcpsrv/pgsql_host_data_source.cc
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/tests/host_unittest.cc
src/share/database/scripts/mysql/dhcpdb_create.mysql
src/share/database/scripts/mysql/upgrade_5.0_to_5.1.sh.in
src/share/database/scripts/pgsql/dhcpdb_create.pgsql
src/share/database/scripts/pgsql/upgrade_3.1_to_3.2.sh.in

index 2be574a095632e7dfe8baaaaecfc26153e675e3e..0210541b28c1912963629c0c98e3516564bc3807 100644 (file)
@@ -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 (file)
index 0000000..93bfa77
--- /dev/null
@@ -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 (file)
index 0000000..bd10aed
--- /dev/null
@@ -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"
+   } ]
+}
+
+}
+
index 3e245beac2d6c6e87161ec8cc1b04a193b05f4f3..e26829017a7d8b32817bf0372af2a8fae5c406d9 100644 (file)
@@ -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.
       </para></note>
 
         <note>
@@ -2568,11 +2570,11 @@ both the command and the response.
       <para>
         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.
       </para>
     </section>
 
index 40e7264caf710d0836218afb3f69337af252cc52..19ec5bc37908336749b30f0eafd66aa9d31c697a 100644 (file)
@@ -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"
index d35faef2f7a9a07a22e7cecbdfec4e700bda5e1b..7cc005ea9f159cf1ffbd14ee399b4da8f451fedc 100644 (file)
@@ -247,6 +247,7 @@ TEST(ParserTest, file) {
                                "backends.json",
                                "cassandra.json",
                                "classify.json",
+                               // "comments.json",
                                "dhcpv4-over-dhcpv6.json",
                                "hooks.json",
                                "leases-expiration.json",
index 298e71647d0593dc03615e324d879f1da9d08d82..959fbe96cc625fb74f8cce4bee48879195649703 100644 (file)
@@ -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"
index bbc969e9e03e8ebf969162f85112dc9941f985ce..c2286bd9abc1a503f7a88886fcd45bd079a856d3 100644 (file)
@@ -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");
index 4b644977cda778e71163186782514f5544cf2820..2f37017064ba455e464cf7554d367c32d4835eb4 100644 (file)
@@ -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));
index dbac205fe2e8f3d0a931210660476d8b3ba75e2d..8f38e43a2825225aed0814c2659a12591fc4f77e 100644 (file)
@@ -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<char*>(&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<char*>(&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<char*>(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<char*>(&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<char*>(&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<char*>(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<char*>(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<char*>(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<char*>(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,
index 580f03dd2a983d112f206dd79ce45282f9cdb006..e955fbd5b9d3600026cb81f070f1f9f3af65a8b4 100644 (file)
@@ -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
index da3d1e2ef9aa5763f36284bc72c8af46f3fd8e9e..b332cbb8d4a99fbec6f35da3ecaa0ef0464555d5 100644 (file)
@@ -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<ElementPtr> networks = shared_networks->listValue();
+        for (auto network = networks.cbegin();
+             network != networks.cend(); ++network) {
+            const std::vector<ElementPtr> 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<ElementPtr> networks = shared_networks->listValue();
+        for (auto network = networks.cbegin();
+             network != networks.cend(); ++network) {
+            const std::vector<ElementPtr> 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
index e5e219e1426f599600e3a5c4336bebb62651236f..bc17bb13f4c71fa4af526bd86f7d20d41efce1a6 100644 (file)
@@ -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\": \"\" "
index 3259648ea7dcc7f290f27d068577391489e1a03f..dc512f731422e3ff1c77033ae57fbd951c51f4c0 100644 (file)
@@ -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;
index 54f81092505a8039521843583dd9727f17598e36..135c08fc24085fe35ec3a06df0b7d229910822e0 100644 (file)
@@ -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;
index 51a50616f9087520d57bea18da79e5b524c83ee8..b73eeef8b38fbc6e03e07e6303734df14c9a7809 100644 (file)
@@ -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;
index 78e212ce5c195de4eb2b64d8491469794a911515..ca1e4d7b8bf2fe64afd351c4d2bf06cd446f8a8b 100644 (file)
@@ -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;