Kea 3.1.0
translator_pool.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10#include <asiolink/io_address.h>
12#include <yang/yang_models.h>
13
14#include <boost/lexical_cast.hpp>
15
16#include <sstream>
17
18using namespace std;
19using namespace isc::data;
20using namespace isc::asiolink;
21using namespace libyang;
22using namespace sysrepo;
23
24namespace isc {
25namespace yang {
26
27TranslatorPool::TranslatorPool(Session session, const string& model)
28 : Translator(session, model),
29 TranslatorOptionData(session, model),
30 TranslatorOptionDataList(session, model) {
31}
32
34TranslatorPool::getPool(DataNode const& data_node) {
35 try {
36 if (model_ == IETF_DHCPV6_SERVER) {
37 return (getPoolIetf6(data_node));
38 } else if ((model_ == KEA_DHCP4_SERVER) ||
39 (model_ == KEA_DHCP6_SERVER)) {
40 return (getPoolKea(data_node));
41 }
42 } catch (Error const& ex) {
44 "getting pool:"
45 << ex.what());
46 }
48 "getPool not implemented for the model: " << model_);
49}
50
53 try {
54 return getPool(findXPath(xpath));
55 } catch (NetconfError const&) {
56 return ElementPtr();
57 }
58}
59
61TranslatorPool::getPoolIetf6(DataNode const& data_node) {
63
64 getMandatoryDivergingLeaf(result, data_node, "pool", "pool-prefix");
65
66 checkAndGetLeaf(result, data_node, "client-class");
67 checkAndGetLeaf(result, data_node, "preferred-lifetime");
68 checkAndGetLeaf(result, data_node, "valid-lifetime");
69
70 checkAndGetDivergingLeaf(result, data_node, "rebind-timer", "rebind-time");
71 checkAndGetDivergingLeaf(result, data_node, "renew-timer", "renew-time");
72
73 // Skip max-addr-count.
74 // Skip pool-id which exists but is not used.
75 // Skip rapid-commit.
76 // Skip start-address - end-address as prefix form is mandatory?
77 // @todo: option-data
78 // No evaluate-additional-classes.
79 // No user-context.
80
81 return (result->empty() ? ElementPtr() : result);
82}
83
85TranslatorPool::getPoolKea(DataNode const& data_node) {
87 ConstElementPtr prefix = getItem(data_node, "prefix");
88 if (prefix) {
89 result->set("pool", prefix);
90 } else {
91 ConstElementPtr start_addr = getItem(data_node, "start-address");
92 ConstElementPtr end_addr = getItem(data_node, "end-address");
93 if (!start_addr || !end_addr) {
94 isc_throw(MissingNode, "getPoolKea requires either prefix or "
95 "both start and end addresses");
96 }
97 ostringstream range;
98 range << start_addr->stringValue() << " - "
99 << end_addr->stringValue();
100 result->set("pool", Element::create(range.str()));
101 }
102 ConstElementPtr options = getOptionDataList(data_node);
103 if (options) {
104 result->set("option-data", options);
105 }
106 checkAndGetLeaf(result, data_node, "ddns-generated-prefix");
107 checkAndGetLeaf(result, data_node, "ddns-override-client-update");
108 checkAndGetLeaf(result, data_node, "ddns-override-no-update");
109 checkAndGetLeaf(result, data_node, "ddns-qualifying-suffix");
110 checkAndGetLeaf(result, data_node, "ddns-replace-client-name");
111 checkAndGetLeaf(result, data_node, "ddns-send-updates");
112 checkAndGetLeaf(result, data_node, "ddns-ttl-percent");
113 checkAndGetLeaf(result, data_node, "ddns-ttl");
114 checkAndGetLeaf(result, data_node, "ddns-ttl-min");
115 checkAndGetLeaf(result, data_node, "ddns-ttl-max");
116 checkAndGetLeaf(result, data_node, "ddns-update-on-renew");
117 checkAndGetLeaf(result, data_node, "ddns-use-conflict-resolution");
118 checkAndGetLeaf(result, data_node, "ddns-conflict-resolution-mode");
119 checkAndGetLeaf(result, data_node, "hostname-char-replacement");
120 checkAndGetLeaf(result, data_node, "hostname-char-set");
121 checkAndGetLeaf(result, data_node, "client-class");
122 checkAndGetLeaf(result, data_node, "client-classes");
123 checkAndGetLeaf(result, data_node, "require-client-classes");
124 checkAndGetLeaf(result, data_node, "evaluate-additional-classes");
125
126 checkAndGetLeaf(result, data_node, "pool-id");
127
128 checkAndGetAndJsonifyLeaf(result, data_node, "user-context");
129
130 return (result->empty() ? ElementPtr() : result);
131}
132
133void
134TranslatorPool::setPool(string const& xpath, ConstElementPtr elem) {
135 try {
136 if (model_ == IETF_DHCPV6_SERVER) {
137 setPoolIetf6(xpath, elem);
138 } else if ((model_ == KEA_DHCP4_SERVER) ||
139 (model_ == KEA_DHCP6_SERVER)) {
140 setPoolKea(xpath, elem);
141 } else {
143 "setPool not implemented for the model: " << model_);
144 }
145 } catch (Error const& ex) {
147 "setting pool '" << elem->str()
148 << "' : " << ex.what());
149 }
150}
151
152void
154 ConstElementPtr pool = elem->get("pool");
155 if (!pool) {
156 isc_throw(BadValue, "setPoolIetf6 requires pool: " << elem->str());
157 }
158 string prefix = pool->stringValue();
159 if (prefix.find("/") == string::npos) {
161 "setPoolIetf only supports pools in prefix (vs range) "
162 "format and was called with '" << prefix << "'");
163 }
164 setItem(xpath + "/pool-prefix", pool, LeafBaseType::String);
165 string addr = prefix.substr(0, prefix.find_first_of(" /"));
166 uint8_t plen = boost::lexical_cast<unsigned>
167 (prefix.substr(prefix.find_last_of(" /") + 1, string::npos));
168 const IOAddress& base(addr);
169 setItem(xpath + "/start-address",
170 Element::create(firstAddrInPrefix(base, plen).toText()),
171 LeafBaseType::String);
172 setItem(xpath + "/end-address",
173 Element::create(lastAddrInPrefix(base, plen).toText()),
174 LeafBaseType::String);
175
176 checkAndSetLeaf(elem, xpath, "client-class", LeafBaseType::String);
177 checkAndSetLeaf(elem, xpath, "preferred-lifetime", LeafBaseType::Uint32);
178 checkAndSetLeaf(elem, xpath, "valid-lifetime", LeafBaseType::Uint32);
179
180 checkAndSetDivergingLeaf(elem, xpath, "rebind-timer", "rebind-time", LeafBaseType::Uint32);
181 checkAndSetDivergingLeaf(elem, xpath, "renew-timer", "renew-time", LeafBaseType::Uint32);
182
183 // Set max address count to disabled.
184 setItem(xpath + "/max-address-count",
185 Element::create("disabled"),
186 LeafBaseType::Enum);
187
188 // Skip max-addr-count.
189 // Skip rapid-commit.
190 // @todo: option-data
191}
192
193void
194TranslatorPool::setPoolKea(string const& xpath, ConstElementPtr elem) {
195 // Set the list element. This is important in case we have no other elements except the keys.
196 setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
197
198 // Skip keys "start-address" and "end-address" since they were set with the
199 // list element in the call above with the LeafBaseType::Unknown parameter.
200
201 ConstElementPtr pool = elem->get("pool");
202 if (!pool) {
203 isc_throw(BadValue, "setPoolKea requires pool: " << elem->str());
204 }
205
206 // Keys are set by setting the list itself.
207 setItem(xpath, ElementPtr(), LeafBaseType::Unknown);
208
209 checkAndSetLeaf(elem, xpath, "ddns-generated-prefix", LeafBaseType::String);
210 checkAndSetLeaf(elem, xpath, "ddns-override-client-update", LeafBaseType::Bool);
211 checkAndSetLeaf(elem, xpath, "ddns-override-no-update", LeafBaseType::Bool);
212 checkAndSetLeaf(elem, xpath, "ddns-qualifying-suffix", LeafBaseType::String);
213 checkAndSetLeaf(elem, xpath, "ddns-replace-client-name", LeafBaseType::String);
214 checkAndSetLeaf(elem, xpath, "ddns-send-updates", LeafBaseType::Bool);
215 checkAndSetLeaf(elem, xpath, "ddns-ttl-percent", LeafBaseType::Dec64);
216 checkAndSetLeaf(elem, xpath, "ddns-ttl", LeafBaseType::Uint32);
217 checkAndSetLeaf(elem, xpath, "ddns-ttl-min", LeafBaseType::Uint32);
218 checkAndSetLeaf(elem, xpath, "ddns-ttl-max", LeafBaseType::Uint32);
219 checkAndSetLeaf(elem, xpath, "ddns-update-on-renew", LeafBaseType::Bool);
220 checkAndSetLeaf(elem, xpath, "ddns-use-conflict-resolution", LeafBaseType::Bool);
221 checkAndSetLeaf(elem, xpath, "ddns-conflict-resolution-mode", LeafBaseType::Enum);
222 checkAndSetLeaf(elem, xpath, "hostname-char-replacement", LeafBaseType::String);
223 checkAndSetLeaf(elem, xpath, "hostname-char-set", LeafBaseType::String);
224 checkAndSetLeaf(elem, xpath, "client-class", LeafBaseType::String);
225 checkAndSetLeafList(elem, xpath, "client-classes", LeafBaseType::String);
226 checkAndSetLeafList(elem, xpath, "require-client-classes", LeafBaseType::String);
227 checkAndSetLeafList(elem, xpath, "evaluate-additional-classes", LeafBaseType::String);
228
229 checkAndSetLeaf(elem, xpath, "pool-id", LeafBaseType::Dec64);
230
231 checkAndSetUserContext(elem, xpath);
232
233 string prefix = pool->stringValue();
234 string start_addr;
235 string end_addr;
236 getAddresses(prefix, start_addr, end_addr);
237 if (prefix.find("/") != string::npos) {
238 setItem(xpath + "/prefix", pool, LeafBaseType::String);
239 }
240 ConstElementPtr options = elem->get("option-data");
241 if (options && !options->empty()) {
242 setOptionDataList(xpath, options);
243 }
244}
245
246void
247TranslatorPool::getAddresses(const string& prefix,
248 string& start_address, string& end_address) {
249 size_t slash = prefix.find("/");
250 if (slash != string::npos) {
251 string addr = prefix.substr(0, prefix.find_first_of(" /"));
252 uint8_t plen = boost::lexical_cast<unsigned>
253 (prefix.substr(prefix.find_last_of(" /") + 1, string::npos));
254 start_address = firstAddrInPrefix(IOAddress(addr), plen).toText();
255 end_address = lastAddrInPrefix(IOAddress(addr), plen).toText();
256 return;
257 }
258 size_t dash = prefix.find("-");
259 if (dash == string::npos) {
261 "getAddresses called with invalid prefix: " << prefix);
262 }
263 start_address = prefix.substr(0, prefix.find_first_of(" -"));
264 end_address = prefix.substr(prefix.find_last_of(" -") + 1, string::npos);
265}
266
267TranslatorPools::TranslatorPools(Session session, const string& model)
268 : Translator(session, model),
269 TranslatorOptionData(session, model),
270 TranslatorOptionDataList(session, model),
271 TranslatorPool(session, model) {
272}
273
275TranslatorPools::getPools(DataNode const& data_node) {
276 try {
277 if (model_ == IETF_DHCPV6_SERVER) {
278 return (getPoolsIetf(data_node));
279 } else if ((model_ == KEA_DHCP4_SERVER) ||
280 (model_ == KEA_DHCP6_SERVER)) {
281 return (getPoolsKea(data_node));
282 }
283 } catch (Error const& ex) {
285 "getting pools:"
286 << ex.what());
287 }
289 "getPools not implemented for the model: " << model_);
290}
291
294 try {
295 return getPools(findXPath(xpath));
296 } catch (NetconfError const&) {
297 return ElementPtr();
298 }
299}
300
302TranslatorPools::getPoolsIetf(DataNode const& data_node) {
303 return getList<TranslatorPool>(data_node, "address-pool", *this,
305}
306
308TranslatorPools::getPoolsKea(DataNode const& data_node) {
309 return getList<TranslatorPool>(data_node, "pool", *this,
311}
312
313void
314TranslatorPools::setPools(string const& xpath, ConstElementPtr elem) {
315 try {
316 if (model_ == IETF_DHCPV6_SERVER) {
317 setPoolsById(xpath, elem);
318 } else if ((model_ == KEA_DHCP4_SERVER) ||
319 (model_ == KEA_DHCP6_SERVER)) {
320 setPoolsByAddresses(xpath, elem);
321 } else {
323 "setPools not implemented for the model: " << model_);
324 }
325 } catch (Error const& ex) {
327 "setting pools '" << elem->str()
328 << "' : " << ex.what());
329 }
330}
331
332void
334 for (size_t i = 0; i < elem->size(); ++i) {
335 ElementPtr pool = elem->getNonConst(i);
336 ostringstream prefix;
337 prefix << xpath << "/address-pool[pool-id='" << i << "']";
338 setPool(prefix.str(), pool);
339 }
340}
341
342void
344 ConstElementPtr elem) {
345 for (size_t i = 0; i < elem->size(); ++i) {
346 ElementPtr pool = elem->getNonConst(i);
347 if (!pool->contains("pool")) {
348 isc_throw(BadValue, "setPoolsByAddresses: missing required pool: "
349 << pool->str());
350 }
351 string pref = pool->get("pool")->stringValue();
352 string start_addr;
353 string end_addr;
354 getAddresses(pref, start_addr, end_addr);
355 ostringstream prefix;
356 prefix << xpath << "/pool[start-address='" << start_addr
357 << "'][end-address='" << end_addr << "']";
358 setPool(prefix.str(), pool);
359 }
360}
361
362} // namespace yang
363} // namespace isc
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when a function is not implemented.
A translator class for converting an option data list between YANG and JSON.
void setOptionDataList(const std::string &xpath, isc::data::ConstElementPtr elem)
Translate and set option data list from JSON to YANG.
TranslatorOptionDataList(sysrepo::Session session, const std::string &model)
Constructor.
isc::data::ConstElementPtr getOptionDataList(libyang::DataNode const &data_node)
Translate option data list from YANG to JSON.
Option data translation between YANG and JSON.
void setPool(const std::string &xpath, isc::data::ConstElementPtr elem)
Translate and set (address) pool from JSON to YANG.
void setPoolKea(const std::string &xpath, isc::data::ConstElementPtr elem)
setPool for kea-dhcp[46]-server.
isc::data::ElementPtr getPoolIetf6(libyang::DataNode const &data_node)
getPool for ietf-dhcpv6-server.
isc::data::ElementPtr getPool(libyang::DataNode const &data_node)
Translate a pool from YANG to JSON.
isc::data::ElementPtr getPoolKea(libyang::DataNode const &data_node)
getPool for kea-dhcp[46]-server.
TranslatorPool(sysrepo::Session session, const std::string &model)
Constructor.
isc::data::ElementPtr getPoolFromAbsoluteXpath(std::string const &xpath)
Translate a pool from YANG to JSON.
static void getAddresses(const std::string &prefix, std::string &start_address, std::string &end_address)
Get start and end addresses from prefix.
void setPoolIetf6(const std::string &xpath, isc::data::ConstElementPtr elem)
setPool for ietf-dhcpv6-server.
void setPoolsByAddresses(const std::string &xpath, isc::data::ConstElementPtr elem)
setPools using address pair.
void setPoolsById(const std::string &xpath, isc::data::ConstElementPtr elem)
setPools using pool-id.
isc::data::ElementPtr getPoolsKea(libyang::DataNode const &data_node)
getPools for kea-dhcp[46]-server.
isc::data::ElementPtr getPoolsFromAbsoluteXpath(std::string const &xpath)
Translate pools from YANG to JSON.
TranslatorPools(sysrepo::Session session, const std::string &model)
Constructor.
void setPools(const std::string &xpath, isc::data::ConstElementPtr elem)
Translate and set (address) pools from JSON to YANG.
isc::data::ElementPtr getPoolsIetf(libyang::DataNode const &data_node)
getPools for ietf-dhcpv6-server.
isc::data::ElementPtr getPools(libyang::DataNode const &data_node)
Translate pools from YANG to JSON.
Between YANG and JSON translator class for basic values.
Definition translator.h:23
void getMandatoryDivergingLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, std::string const &name, std::string const &yang_name) const
Retrieves a child YANG data node identified by one name from the given parent YANG container node and...
isc::data::ElementPtr getList(libyang::DataNode const &data_node, std::string const &xpath, T &t, isc::data::ElementPtr(T::*f)(libyang::DataNode const &)) const
Retrieve a list as ElementPtr from sysrepo from a certain xpath.
Definition translator.h:274
void checkAndSetLeaf(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
Definition translator.cc:63
isc::data::ElementPtr getItem(libyang::DataNode const &data_node, std::string const &xpath) const
Translate a basic value from YANG to JSON for a given xpath that is relative to the given source node...
libyang::DataNode findXPath(std::string const &xpath) const
Retrieves a YANG data node by xpath.
void checkAndGetLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, std::string const &name) const
Retrieves a child YANG data node identified by name from the given parent YANG container node and sto...
Definition translator.cc:32
void checkAndSetUserContext(isc::data::ConstElementPtr const &from, std::string const &xpath)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
Definition translator.cc:99
void checkAndGetDivergingLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, std::string const &name, std::string const &yang_name) const
Retrieves a child YANG data node identified by name from the given parent YANG container node and sto...
Definition translator.cc:42
void setItem(const std::string &xpath, isc::data::ConstElementPtr const elem, libyang::LeafBaseType const type)
Translate and set basic value from JSON to YANG.
std::string model_
The model.
Definition translator.h:427
void checkAndSetDivergingLeaf(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, std::string const &yang_name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
Definition translator.cc:74
void checkAndGetAndJsonifyLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, const std::string &name) const
Retrieves a child YANG data node identified by name from the given parent YANG container node,...
Definition translator.cc:53
void checkAndSetLeafList(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath as a leaf-list.
Definition translator.cc:86
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
Defines the logger used by the top-level component of kea-lfc.
Missing node error.
Generic NETCONF error.