]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3907] Progressed some more with YANG modules
authorAndrei Pavel <andrei@isc.org>
Fri, 20 Jun 2025 11:57:19 +0000 (14:57 +0300)
committerRazvan Becheriu <razvan@isc.org>
Fri, 20 Jun 2025 13:29:10 +0000 (13:29 +0000)
doc/examples/kea4/all-keys-netconf.json
doc/examples/kea6/all-keys-netconf.json
src/lib/yang/tests/translator_control_socket_unittests.cc
src/lib/yang/tests/translator_database_unittests.cc
src/lib/yang/translator_config.cc
src/lib/yang/translator_control_socket.cc
src/lib/yang/translator_control_socket.h
src/lib/yang/translator_database.cc
src/lib/yang/translator_database.h
src/share/yang/modules/hashes/kea-types@2025-06-25.hash
src/share/yang/modules/kea-types@2025-06-25.yang

index e350af1bd2d0cd6d17d385b6ce57de791fc6aa16..92b09359cadcaf95dd0dc1163dfe0d74c4b5479e 100644 (file)
 
                             // If password is not specified an empty
                             // password is used.
-                            "password": "1234"
+                            "password": "1234",
+                            "password-file": "",
+                            "user-file": ""
                         },
 
                         // This specifies a hidden client.
                             // The user id is the content of the
                             // file /usr/local/share/kea/kea-creds/hiddenu.
                             "user-file": "hiddenu",
+                            "user": "",
 
                             // The password is the content of the
                             // file /usr/local/share/kea/kea-creds/hiddenp.
-                            "password-file": "hiddenp"
+                            "password-file": "hiddenp",
+                            "password": ""
                         },
 
                         // This specifies a hidden client using a
                             // The secret is the content of the file
                             // /usr/local/share/kea/kea-creds/hiddens which must be in
                             // the <user-id>:<password> format.
-                            "password-file": "hiddens"
+                            "password-file": "hiddens",
+                            "user-file": "",
+                            "user": "",
+                            "password": ""
                         }
                     ]
                 }
index e070989d04af4297e782b013145b446173fcde50..ccb8ccde0a8f069010d2d52579013c91f94affb4 100644 (file)
 
                             // If password is not specified an empty
                             // password is used.
-                            "password": "1234"
+                            "password": "1234",
+                            "password-file": "",
+                            "user-file": ""
                         },
 
                         // This specifies a hidden client.
                             // The user id is the content of the
                             // file /usr/local/share/kea/kea-creds/hiddenu.
                             "user-file": "hiddenu",
+                            "user": "",
 
                             // The password is the content of the
                             // file /usr/local/share/kea/kea-creds/hiddenp.
-                            "password-file": "hiddenp"
+                            "password-file": "hiddenp",
+                            "password": ""
                         },
 
                         // This specifies a hidden client using a
                             // The secret is the content of the file
                             // /usr/local/share/kea/kea-creds/hiddens which must be in
                             // the <user-id>:<password> format.
-                            "password-file": "hiddens"
+                            "password-file": "hiddens",
+                            "user-file": "",
+                            "user": "",
+                            "password": ""
                         }
                     ]
                 }
index 526bfbf896a8ae87f7259d87edc5ad6fa9a68d0f..2777c023f94cf20fe503498930f7134997790fee 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 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
index 4203bcb56d677664679e2499baea04b15ba82c10..35b9170cc1f7ad0d1a729d5d75625bd345b2c0ce 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2025 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
@@ -92,7 +92,7 @@ TEST_F(TranslatorDatabaseTestv4, set) {
     ElementPtr database = Element::createMap();
     database->set("type", Element::create("memfile"));
     database->set("lfc-interval", Element::create(3600));
-    ASSERT_NO_THROW_LOG(translator_->setDatabase(xpath, database));
+    ASSERT_NO_THROW_LOG(translator_->setDatabase(xpath, database, /* has_mandatory_key = */ false));
 
     // Get it back.
     ConstElementPtr got;
@@ -125,7 +125,7 @@ TEST_F(TranslatorDatabaseTestv4, setEmpty) {
     sess_->applyChanges();
 
     // Reset to empty.
-    ASSERT_NO_THROW_LOG(translator_->setDatabase(xpath, ConstElementPtr()));
+    ASSERT_NO_THROW_LOG(translator_->setDatabase(xpath, ConstElementPtr(), /* has_mandatory_key = */ false));
 
     // Get it back.
     ConstElementPtr database;
@@ -280,7 +280,7 @@ TEST_F(TranslatorDatabasesTestv4, setEmpty) {
     sess_->applyChanges();
 
     // Reset to empty.
-    EXPECT_NO_THROW_LOG(translator_->setDatabase(xdatabase, ConstElementPtr()));
+    EXPECT_NO_THROW_LOG(translator_->setDatabase(xdatabase, ConstElementPtr(), /* has_mandatory_key = */ true));
 
     // Get empty.
     ConstElementPtr databases;
@@ -313,7 +313,7 @@ TEST_F(TranslatorDatabasesTestv4, setEmpties) {
     sess_->applyChanges();
 
     // Reset to empty.
-    EXPECT_NO_THROW_LOG(translator_->setDatabases(xdatabase, ConstElementPtr()));
+    EXPECT_NO_THROW_LOG(translator_->setDatabase(xdatabase, ConstElementPtr(), /* has_mandatory_key = */ true));
 
     // Get empty.
     ConstElementPtr databases;
index d695a740d7b6dffca54900e66322be97142f4dae..bdd098c5b9d8f5cfe6dba907df6a5f551335a833 100644 (file)
@@ -632,7 +632,7 @@ TranslatorConfig::setServerKeaDhcpCommon(string const& xpath,
 
     ConstElementPtr database = elem->get("lease-database");
     if (database && !database->empty()) {
-        setDatabase(xpath + "/lease-database", database);
+        setDatabase(xpath + "/lease-database", database, /* has_mandatory_key = */ false);
     }
 
     ConstElementPtr loggers = elem->get("loggers");
index cf4c44f3af6ebde09ad59d18c28e211e07fcf4b6..b997ae7f47ca8044ed112ae53b9e0723dab94ae8 100644 (file)
@@ -66,8 +66,8 @@ TranslatorControlSocket::getControlSocketsKea(DataNode const& data_node) {
 ElementPtr
 TranslatorControlSocket::getControlSocketKea(DataNode const& data_node) {
     ElementPtr result(Element::createMap());
-    getMandatoryLeaf(result, data_node, "socket-type");
     checkAndGetLeaf(result, data_node, "socket-name");
+    checkAndGetLeaf(result, data_node, "socket-type");
     if (model_ != KEA_CTRL_AGENT) {
         checkAndGetLeaf(result, data_node, "socket-address");
         checkAndGetLeaf(result, data_node, "socket-port");
@@ -86,7 +86,7 @@ TranslatorControlSocket::getControlSocketKea(DataNode const& data_node) {
                             authentication = Element::createMap();
                         }
 
-                        getMandatoryDivergingLeaf(authentication, node, "type", "auth-type");
+                        checkAndGetDivergingLeaf(authentication, node, "type", "auth-type");
                         checkAndGetLeaf(authentication, node, "realm");
                         checkAndGetLeaf(authentication, node, "directory");
                         ConstElementPtr clients = getControlSocketAuthenticationClients(node);
@@ -153,7 +153,7 @@ TranslatorControlSocket::setControlSockets(string const& xpath,
             (model_ == KEA_DHCP_DDNS)) {
             setControlSocketsKea(xpath, elem);
         } else if (model_ == KEA_CTRL_AGENT) {
-            setControlSocketKea(xpath, elem);
+            setControlSocketKea(xpath, elem, /* has_mandatory_key = */ true);
         } else {
           isc_throw(NotImplemented,
                     "setControlSocket not implemented for the model: "
@@ -174,7 +174,7 @@ TranslatorControlSocket::setControlSocket(string const& xpath,
             (model_ == KEA_DHCP6_SERVER) ||
             (model_ == KEA_DHCP_DDNS) ||
             (model_ == KEA_CTRL_AGENT)) {
-            setControlSocketKea(xpath, elem);
+            setControlSocketKea(xpath, elem, /* has_mandatory_key = */ false);
         } else {
           isc_throw(NotImplemented,
                     "setControlSocket not implemented for the model: "
@@ -202,28 +202,37 @@ TranslatorControlSocket::setControlSocketsKea(string const& xpath,
         string type = control_socket->get("socket-type")->stringValue();
         ostringstream key;
         key << xpath << "[socket-type='" << type << "']";
-        setControlSocketKea(key.str(), control_socket);
+        setControlSocketKea(key.str(), control_socket, /* has_mandatory_key = */ true);
     }
 }
 
 void
-TranslatorControlSocket::setControlSocketKea(string const& xpath, ConstElementPtr elem) {
+TranslatorControlSocket::setControlSocketKea(string const& xpath,
+                                             ConstElementPtr elem,
+                                             bool has_mandatory_key) {
     if (!elem) {
         deleteItem(xpath);
         return;
     }
 
+    if (has_mandatory_key) {
+        // Set the list element. This is important in case we have no other elements except the key.
+        setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+    } else {
+        checkAndSetLeaf(elem, xpath, "socket-type", LeafBaseType::String);
+    }
+
     checkAndSetLeaf(elem, xpath, "socket-name", LeafBaseType::String);
     if (model_ != KEA_CTRL_AGENT) {
         checkAndSetLeaf(elem, xpath, "socket-address", LeafBaseType::String);
-        checkAndSetLeaf(elem, xpath, "socket-port", LeafBaseType::Uint32);
+        checkAndSetLeaf(elem, xpath, "socket-port", LeafBaseType::Uint16);
         checkAndSetLeaf(elem, xpath, "trust-anchor", LeafBaseType::String);
         checkAndSetLeaf(elem, xpath, "cert-file", LeafBaseType::String);
         checkAndSetLeaf(elem, xpath, "key-file", LeafBaseType::String);
         checkAndSetLeaf(elem, xpath, "cert-required", LeafBaseType::Bool);
         ConstElementPtr authentication = elem->get("authentication");
         if (authentication && !authentication->empty()) {
-            setMandatoryDivergingLeaf(authentication, xpath , "type", "auth-type", LeafBaseType::String);
+            setMandatoryDivergingLeaf(authentication, xpath +"/authentication" , "type", "auth-type", LeafBaseType::String);
             checkAndSetLeaf(authentication, xpath + "/authentication", "realm", LeafBaseType::String);
             checkAndSetLeaf(authentication, xpath + "/authentication", "directory", LeafBaseType::String);
             ConstElementPtr clients = authentication->get("clients");
@@ -231,13 +240,9 @@ TranslatorControlSocket::setControlSocketKea(string const& xpath, ConstElementPt
         }
         ConstElementPtr http_headers = elem->get("http-headers");
         if (http_headers && !http_headers->empty()) {
-            for (size_t i = 0; i < http_headers->size(); ++i) {
-                ElementPtr header = elem->getNonConst(i);
-                // setHeader
-            }
+            setControlSocketHttpHeaders(xpath + "/http-headers", http_headers);
         }
     }
-    setMandatoryLeaf(elem, xpath, "socket-type", LeafBaseType::Enum);
     checkAndSetUserContext(elem, xpath);
 }
 
@@ -271,7 +276,7 @@ TranslatorControlSocket::setControlSocketAuthenticationClients(string const& xpa
         if (password_file) {
             password_file_str = password_file->stringValue();
         }
-        key << xpath << "[user='" << user_str << "'][password=']" << password_str
+        key << xpath << "[user='" << user_str << "'][password='" << password_str
                      << "'][user-file='" << user_file_str << "'][password-file='"
                      << password_file_str << "']";
         setControlSocketAuthenticationClient(key.str(), client);
@@ -291,13 +296,13 @@ TranslatorControlSocket::setControlSocketHttpHeaders(const std::string& xpath,
         deleteItem(xpath);
         return;
     }
-    for (size_t i = 0; i < elem->size(); ++i) {
-        ElementPtr header = elem->getNonConst(i);
-        ostringstream key;
-        if (!header->contains("name")) {
+    for (ConstElementPtr header : elem->listValue()) {
+        ConstElementPtr name(header->get("name"));
+        if (!name) {
             isc_throw(BadValue, "http header without name: " << header->str());
         }
-        key << xpath << "[name='" << header->stringValue() << "']";
+        ostringstream key;
+        key << xpath << "[name='" << name->stringValue() << "']";
         setControlSocketHttpHeader(key.str(), header);
     }
 }
@@ -309,7 +314,6 @@ TranslatorControlSocket::setControlSocketHttpHeader(const std::string& xpath,
 
     checkAndSetLeaf(elem, xpath, "value", LeafBaseType::String);
     checkAndSetUserContext(elem, xpath);
-    setMandatoryLeaf(elem, xpath, "name", LeafBaseType::Enum);
 }
 
 }  // namespace yang
index 4ee8b17ec451cbb4bcc2a50f3508edd432c4bf76..d4d77b8577d1714fcacf00b2b1c34971e7c60cf5 100644 (file)
@@ -187,8 +187,9 @@ protected:
     ///
     /// @param xpath The xpath of the control socket.
     /// @param elem The JSON element.
+    /// @param has_mandatory_key Whether this specific database instance has a mandatory key.
     /// @throw BadValue on control socket without socket type or name.
-    void setControlSocketKea(const std::string& xpath, isc::data::ConstElementPtr elem);
+    void setControlSocketKea(const std::string& xpath, isc::data::ConstElementPtr elem, bool has_mandatory_key);
 
     /// @brief setControlSocketAuthenticationClients for kea models.
     ///
index cb5e67ba51b949ea34da5e15daed965901ed8e21..37427dae7eb569336734a1e45e3e753ee7a847bf 100644 (file)
@@ -51,6 +51,7 @@ ElementPtr
 TranslatorDatabase::getDatabaseKea(DataNode const& data_node) {
     ElementPtr result = Element::createMap();
 
+    checkAndGetDivergingLeaf(result, data_node, "type", "database-type");
     checkAndGetLeaf(result, data_node, "cert-file");
     checkAndGetLeaf(result, data_node, "cipher-list");
     checkAndGetLeaf(result, data_node, "connect-timeout");
@@ -79,11 +80,11 @@ TranslatorDatabase::getDatabaseKea(DataNode const& data_node) {
 }
 
 void
-TranslatorDatabase::setDatabase(string const& xpath, ConstElementPtr elem) {
+TranslatorDatabase::setDatabase(string const& xpath, ConstElementPtr elem, bool has_mandatory_key) {
     try {
         if ((model_ == KEA_DHCP4_SERVER) ||
             (model_ == KEA_DHCP6_SERVER)) {
-            setDatabaseKea(xpath, elem);
+            setDatabaseKea(xpath, elem, has_mandatory_key);
         } else {
             isc_throw(NotImplemented,
                       "setDatabase not implemented for the model: " << model_);
@@ -96,8 +97,18 @@ TranslatorDatabase::setDatabase(string const& xpath, ConstElementPtr elem) {
 }
 
 void
-TranslatorDatabase::setDatabaseKea(string const& xpath, ConstElementPtr elem) {
-    setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+TranslatorDatabase::setDatabaseKea(string const& xpath, ConstElementPtr elem, bool has_mandatory_key) {
+    if (!elem) {
+        deleteItem(xpath);
+        return;
+    }
+
+    if (has_mandatory_key) {
+        // Set the list element. This is important in case we have no other elements except the key.
+        setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
+    } else {
+        checkAndSetDivergingLeaf(elem, xpath, "type", "database-type", LeafBaseType::String);
+    }
 
     checkAndSetLeaf(elem, xpath, "connect-timeout", LeafBaseType::Uint32);
     checkAndSetLeaf(elem, xpath, "cert-file", LeafBaseType::String);
@@ -194,7 +205,7 @@ TranslatorDatabases::setDatabasesKea(string const& xpath,
         string type = database->get("type")->stringValue();
         ostringstream key;
         key << xpath << "[database-type='" << type << "']";
-        setDatabase(key.str(), database);
+        setDatabase(key.str(), database, /* has_mandatory_key = */ true);
     }
 }
 
index 7866427428376de544b14714bc3391dc8fd6ebb7..909f7fec8e3fa374bdedc44e4b7c6c9b0074a613 100644 (file)
@@ -141,7 +141,10 @@ public:
     ///
     /// @param xpath The xpath of the database access.
     /// @param elem The JSON element.
-    void setDatabase(const std::string& xpath, isc::data::ConstElementPtr elem);
+    /// @param has_mandatory_key Whether this specific database instance has a mandatory key.
+    void setDatabase(const std::string& xpath,
+                     isc::data::ConstElementPtr elem,
+                     bool has_mandatory_key);
 
 protected:
     /// @brief getDatabase JSON for kea-dhcp[46]-server models.
@@ -157,8 +160,11 @@ protected:
     ///
     /// @param xpath The xpath of the database access.
     /// @param elem The JSON element.
+    /// @param has_mandatory_key Whether this specific database instance has a mandatory key.
     /// @throw BadValue on database without type,
-    void setDatabaseKea(const std::string& xpath, isc::data::ConstElementPtr elem);
+    void setDatabaseKea(const std::string& xpath,
+                        isc::data::ConstElementPtr elem,
+                        bool has_mandatory_key);
 };  // TranslatorDatabase
 
 /// @brief A translator class for converting a database access list between
index d9aa537f5027dbee255791e3acda82f5c7f3b158..d5437bc1beaf902c70ba69d1494576862d188969 100644 (file)
@@ -1 +1 @@
-68739faa231a48c837adb3b306858b5656a1439b39b675f601d21515278d8747
+160eb58d10c1c29ab40763f69b9d905644ee767badfdceee6bdd730e9a6071f6
index ccfff985e9e1b9c231e27195548c6187c15dd793..0442731b21ff0d3133a7d398c9ba9d900241bd0d 100644 (file)
@@ -103,7 +103,7 @@ module kea-types {
       description "HTTP/HTTPS socket address.";
     }
     leaf socket-port {
-      type string;
+      type uint16;
       description "HTTP/HTTPS socket port.";
     }
     uses control-socket-tls;
@@ -162,16 +162,15 @@ module kea-types {
   grouping authentication {
     description "HTTP authentication.";
     container authentication {
-      grouping auth-type {
-        leaf auth-type {
-          type enumeration {
-            enum "basic" {
-              description "Basic HTTP authentication";
-            }
+      presence "";
+      leaf auth-type {
+        type enumeration {
+          enum "basic" {
+            description "Basic HTTP authentication";
           }
-          description "HTTP authentication type.";
-          mandatory true;
         }
+        description "HTTP authentication type.";
+        mandatory true;
       }
       leaf realm {
         type string;
@@ -181,8 +180,8 @@ module kea-types {
         type string;
         description "HTTP authentication directory.";
       }
+      uses clients;
     }
-    uses clients;
   }
 
   grouping hooks-libraries {