Kea  1.5.0
translator_pool.cc
Go to the documentation of this file.
1 // Copyright (C) 2018 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 
9 #include <asiolink/io_address.h>
11 #include <yang/adaptor.h>
12 #include <yang/translator_pool.h>
13 #include <yang/yang_models.h>
14 #include <boost/lexical_cast.hpp>
15 #include <sstream>
16 
17 using namespace std;
18 using namespace isc::data;
19 using namespace isc::asiolink;
20 using namespace isc::dhcp;
21 #ifndef HAVE_PRE_0_7_6_SYSREPO
22 using namespace sysrepo;
23 #endif
24 
25 namespace isc {
26 namespace yang {
27 
28 TranslatorPool::TranslatorPool(S_Session session, const string& model)
29  : TranslatorBasic(session, model),
30  TranslatorOptionData(session, model),
31  TranslatorOptionDataList(session, model) {
32 }
33 
35 }
36 
38 TranslatorPool::getPool(const string& xpath) {
39  try {
40  if (model_ == IETF_DHCPV6_SERVER) {
41  return (getPoolIetf6(xpath));
42  } else if ((model_ == KEA_DHCP4_SERVER) ||
43  (model_ == KEA_DHCP6_SERVER)) {
44  return (getPoolKea(xpath));
45  }
46  } catch (const sysrepo_exception& ex) {
48  "sysrepo error getting pool at '" << xpath
49  << "': " << ex.what());
50  }
52  "getPool not implemented for the model: " << model_);
53 }
54 
56 TranslatorPool::getPoolIetf6(const string& xpath) {
57  ElementPtr result = Element::createMap();
58  // Skip pool-id which exists but is not used.
59  ConstElementPtr pool = getItem(xpath + "/pool-prefix");
60  if (!pool) {
61  isc_throw(BadValue, "getPoolIetf6 requires pool prefix at " << xpath);
62  }
63  result->set("pool", pool);
64  // Ignore start-address - end-address as prefix form is mandatory?
65  ConstElementPtr guard = getItem(xpath + "/client-class");
66  if (guard) {
67  result->set("client-class", guard);
68  }
69  ConstElementPtr valid_lifetime = getItem(xpath + "/valid-lifetime");
70  if (valid_lifetime) {
71  result->set("valid-lifetime", valid_lifetime);
72  }
73  ConstElementPtr preferred_lifetime =
74  getItem(xpath + "/preferred-lifetime");
75  if (preferred_lifetime) {
76  result->set("preferred-lifetime", preferred_lifetime);
77  }
78  ConstElementPtr renew_time = getItem(xpath + "/renew-time");
79  if (renew_time) {
80  result->set("renew-timer", renew_time);
81  }
82  ConstElementPtr rebind_time = getItem(xpath + "/rebind-time");
83  if (rebind_time) {
84  result->set("rebind-timer", rebind_time);
85  }
86  // Skip max-addr-count
87  // @todo: option-data
89  // Skip rapid-commit.
90  return (result);
91 }
92 
94 TranslatorPool::getPoolKea(const string& xpath) {
95  ElementPtr result = Element::createMap();
96  ConstElementPtr prefix = getItem(xpath + "/prefix");
97  if (prefix) {
98  result->set("pool", prefix);
99  } else {
100  ConstElementPtr start_addr = getItem(xpath + "/start-address");
101  ConstElementPtr end_addr = getItem(xpath + "/end-address");
102  if (!start_addr || !end_addr) {
103  isc_throw(BadValue, "getPoolKea requires either prefix or "
104  "both start and end addresses at " << xpath);
105  }
106  ostringstream range;
107  range << start_addr->stringValue() << " - "
108  << end_addr->stringValue();
109  result->set("pool", Element::create(range.str()));
110  }
111  ConstElementPtr options = getOptionDataList(xpath);
112  if (options && (options->size() > 0)) {
113  result->set("option-data", options);
114  }
115  ConstElementPtr guard = getItem(xpath + "/client-class");
116  if (guard) {
117  result->set("client-class", guard);
118  }
119  ConstElementPtr required = getItems(xpath + "/require-client-classes");
120  if (required && (required->size() > 0)) {
121  result->set("require-client-classes", required);
122  }
123  ConstElementPtr context = getItem(xpath + "/user-context");
124  if (context) {
125  result->set("user-context", Element::fromJSON(context->stringValue()));
126  }
127  return (result);
128 }
129 
130 void
131 TranslatorPool::setPool(const string& xpath, ConstElementPtr elem) {
132  try {
133  if (model_ == IETF_DHCPV6_SERVER) {
134  setPoolIetf6(xpath, elem);
135  } else if ((model_ == KEA_DHCP4_SERVER) ||
136  (model_ == KEA_DHCP6_SERVER)) {
137  setPoolKea(xpath, elem);
138  } else {
140  "setPool not implemented for the model: " << model_);
141  }
142  } catch (const sysrepo_exception& ex) {
144  "sysrepo error setting pool '" << elem->str()
145  << "' at '" << xpath << "': " << ex.what());
146  }
147 }
148 
149 void
150 TranslatorPool::setPoolIetf6(const string& xpath, ConstElementPtr elem) {
151  ConstElementPtr pool = elem->get("pool");
152  if (!pool) {
153  isc_throw(BadValue, "setPoolIetf6 requires pool: " << elem->str());
154  }
155  string prefix = pool->stringValue();
156  if (prefix.find("/") == string::npos) {
158  "setPoolIetf only supports pools in prefix (vs range) "
159  "format and was called with '" << prefix << "'");
160  }
161  setItem(xpath + "/pool-prefix", pool, SR_STRING_T);
162  string addr = prefix.substr(0, prefix.find_first_of(" /"));
163  uint8_t plen = boost::lexical_cast<unsigned>
164  (prefix.substr(prefix.find_last_of(" /") + 1, string::npos));
165  const IOAddress& base(addr);
166  setItem(xpath + "/start-address",
167  Element::create(firstAddrInPrefix(base, plen).toText()),
168  SR_STRING_T);
169  setItem(xpath + "/end-address",
170  Element::create(lastAddrInPrefix(base, plen).toText()),
171  SR_STRING_T);
172  ConstElementPtr valid_lifetime = elem->get("valid-lifetime");
173  if (valid_lifetime) {
174  setItem(xpath + "/valid-lifetime", valid_lifetime, SR_UINT32_T);
175  }
176  ConstElementPtr preferred_lifetime = elem->get("preferred-lifetime");
177  if (preferred_lifetime) {
178  setItem(xpath + "/preferred-lifetime",
179  preferred_lifetime, SR_UINT32_T);
180  }
181  ConstElementPtr renew_timer = elem->get("renew-timer");
182  if (renew_timer) {
183  setItem(xpath + "/renew-time", renew_timer, SR_UINT32_T);
184  }
185  ConstElementPtr rebind_timer = elem->get("rebind-timer");
186  if (rebind_timer) {
187  setItem(xpath + "/rebind-time", rebind_timer, SR_UINT32_T);
188  }
189  // skip rapid-commit
190  ConstElementPtr guard = elem->get("client-class");
191  if (guard) {
192  setItem(xpath + "/client-class", guard, SR_STRING_T);
193  }
194  // skip max-addr-count
195  // @todo option-data
196  // Set max address count to disabled.
197  setItem(xpath + "/max-address-count",
198  Element::create(string("disabled")),
199  SR_ENUM_T);
200 }
201 
202 void
203 TranslatorPool::setPoolKea(const string& xpath, ConstElementPtr elem) {
204  ConstElementPtr pool = elem->get("pool");
205  if (!pool) {
206  isc_throw(BadValue, "setPoolKea requires pool: " << elem->str());
207  }
208  bool created = false;
209  string prefix = pool->stringValue();
210  string start_addr;
211  string end_addr;
212  getAddresses(prefix, start_addr, end_addr);
213  if (prefix.find("/") != string::npos) {
214  setItem(xpath + "/prefix", pool, SR_STRING_T);
215  created = true;
216  }
217  // Skip start-address and end-address as are the keys.
218  ConstElementPtr options = elem->get("option-data");
219  if (options && (options->size() > 0)) {
220  setOptionDataList(xpath, options);
221  created = true;
222  }
223  ConstElementPtr guard = elem->get("client-class");
224  if (guard) {
225  setItem(xpath + "/client-class", guard, SR_STRING_T);
226  created = true;
227  }
228  ConstElementPtr required = elem->get("require-client-classes");
229  if (required && (required->size() > 0)) {
230  for (ConstElementPtr rclass : required->listValue()) {
231  setItem(xpath + "/require-client-classes", rclass, SR_STRING_T);
232  created = true;
233  }
234  }
235  ConstElementPtr context = Adaptor::getContext(elem);
236  if (context) {
237  setItem(xpath + "/user-context", Element::create(context->str()),
238  SR_STRING_T);
239  created = true;
240  }
241  // There is no mandatory fields outside the keys so force creation.
242  if (!created) {
243  ConstElementPtr list = Element::createList();
244  setItem(xpath, list, SR_LIST_T);
245  }
246 }
247 
248 void
249 TranslatorPool::getAddresses(const string& prefix,
250  string& start_address, string& end_address) {
251  size_t slash = prefix.find("/");
252  if (slash != string::npos) {
253  string addr = prefix.substr(0, prefix.find_first_of(" /"));
254  uint8_t plen = boost::lexical_cast<unsigned>
255  (prefix.substr(prefix.find_last_of(" /") + 1, string::npos));
256  start_address = firstAddrInPrefix(IOAddress(addr), plen).toText();
257  end_address = lastAddrInPrefix(IOAddress(addr), plen).toText();
258  return;
259  }
260  size_t dash = prefix.find("-");
261  if (dash == string::npos) {
263  "getAddresses called with invalid prefix: " << prefix);
264  }
265  start_address = prefix.substr(0, prefix.find_first_of(" -"));
266  end_address = prefix.substr(prefix.find_last_of(" -") + 1, string::npos);
267 }
268 
269 TranslatorPools::TranslatorPools(S_Session session, const string& model)
270  : TranslatorBasic(session, model),
271  TranslatorOptionData(session, model),
272  TranslatorOptionDataList(session, model),
273  TranslatorPool(session, model) {
274 }
275 
277 }
278 
280 TranslatorPools::getPools(const string& xpath) {
281  try {
282  if (model_ == IETF_DHCPV6_SERVER) {
283  return (getPoolsIetf(xpath));
284  } else if ((model_ == KEA_DHCP4_SERVER) ||
285  (model_ == KEA_DHCP6_SERVER)) {
286  return (getPoolsKea(xpath));
287  }
288  } catch (const sysrepo_exception& ex) {
290  "sysrepo error getting pools at '" << xpath
291  << "': " << ex.what());
292  }
294  "getPools not implemented for the model: " << model_);
295 }
296 
298 TranslatorPools::getPoolsIetf(const string& xpath) {
299  ElementPtr result = Element::createList();
300  S_Iter_Value iter = getIter(xpath + "/address-pool");
301  if (!iter) {
302  // Can't happen.
303  isc_throw(Unexpected, "getPoolsIetf can't get iterator: " << xpath);
304  }
305  for (;;) {
306  const string& pool = getNext(iter);
307  if (pool.empty()) {
308  break;
309  }
310  result->add(getPool(pool));
311  }
312  return (result);
313 }
314 
316 TranslatorPools::getPoolsKea(const string& xpath) {
317  ElementPtr result = Element::createList();
318  S_Iter_Value iter = getIter(xpath + "/pool");
319  if (!iter) {
320  // Can't happen.
321  isc_throw(Unexpected, "getPoolsKea can't get iterator: " << xpath);
322  }
323  for (;;) {
324  const string& pool = getNext(iter);
325  if (pool.empty()) {
326  break;
327  }
328  result->add(getPool(pool));
329  }
330  return (result);
331 }
332 
333 void
334 TranslatorPools::setPools(const string& xpath, ConstElementPtr elem) {
335  try {
336  if (model_ == IETF_DHCPV6_SERVER) {
337  setPoolsById(xpath, elem);
338  } else if ((model_ == KEA_DHCP4_SERVER) ||
339  (model_ == KEA_DHCP6_SERVER)) {
340  setPoolsByAddresses(xpath, elem);
341  } else {
343  "setPools not implemented for the model: " << model_);
344  }
345  } catch (const sysrepo_exception& ex) {
347  "sysrepo error setting pools '" << elem->str()
348  << "' at '" << xpath << "': " << ex.what());
349  }
350 }
351 
352 void
353 TranslatorPools::setPoolsById(const string& xpath, ConstElementPtr elem) {
354  for (size_t i = 0; i < elem->size(); ++i) {
355  ConstElementPtr pool = elem->get(i);
356  ostringstream prefix;
357  prefix << xpath << "/address-pool[pool-id='" << i << "']";
358  setPool(prefix.str(), pool);
359  }
360 }
361 
362 void
364  ConstElementPtr elem) {
365  for (size_t i = 0; i < elem->size(); ++i) {
366  ConstElementPtr pool = elem->get(i);
367  if (!pool->contains("pool")) {
368  isc_throw(BadValue, "setPoolsByAddresses: missing required pool: "
369  << pool->str());
370  }
371  string pref = pool->get("pool")->stringValue();
372  string start_addr;
373  string end_addr;
374  getAddresses(pref, start_addr, end_addr);
375  ostringstream prefix;
376  prefix << xpath << "/pool[start-address='" << start_addr
377  << "'][end-address='" << end_addr << "']";
378  setPool(prefix.str(), pool);
379  }
380 }
381 
382 }; // end of namespace isc::yang
383 }; // end of namespace isc
isc::data::ElementPtr getPool(const std::string &xpath)
Get and translate a pool from YANG to JSON.
isc::data::ElementPtr getItems(const std::string &xpath)
Get and translate a list of basic values from YANG to JSON.
Definition: translator.cc:119
A generic exception that is thrown when a function is not implemented.
isc::data::ElementPtr getPoolIetf6(const std::string &xpath)
getPool for ietf-dhcpv6-server.
virtual ~TranslatorPools()
Destructor.
isc::data::ElementPtr getPoolsKea(const std::string &xpath)
getPools for kea-dhcp[46]-server.
isc::data::ElementPtr getPoolsIetf(const std::string &xpath)
getPools for ietf-dhcpv6-server.
Between YANG and JSON translator class for basic values.
Definition: translator.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
This code is based on similar code from the Dibbler project.
void setPool(const std::string &xpath, isc::data::ConstElementPtr elem)
Translate and set (address) pool from JSON to YANG.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
sysrepo::S_Iter_Value getIter(const std::string &xpath)
List iterator methods keeping the session private.
Definition: translator.cc:278
static void getAddresses(const std::string &prefix, std::string &start_address, std::string &end_address)
Get start and end addresses from prefix.
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress &prefix, uint8_t len)
returns a last address in a given prefix
void setPools(const std::string &xpath, isc::data::ConstElementPtr elem)
Translate and set (address) pools from JSON to YANG.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
void setItem(const std::string &xpath, isc::data::ConstElementPtr elem, sr_type_t type)
Translate and set basic value from JSON to YANG.
Definition: translator.cc:250
void setPoolsById(const std::string &xpath, isc::data::ConstElementPtr elem)
setPools using pool-id.
TranslatorPools(sysrepo::S_Session session, const std::string &model)
Constructor.
void setPoolKea(const std::string &xpath, isc::data::ConstElementPtr elem)
setPool for kea-dhcp[46]-server.
A translator class for converting an option data list between YANG and JSON.
A generic exception that is thrown when an unexpected error condition occurs.
std::string model_
The model.
Definition: translator.h:132
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
std::string getNext(sysrepo::S_Iter_Value iter)
Get xpath of the next YANG list item.
Definition: translator.cc:283
static isc::data::ConstElementPtr getContext(isc::data::ConstElementPtr parent)
Get user context.
Definition: adaptor.cc:25
Defines the logger used by the top-level component of kea-dhcp-ddns.
isc::data::ConstElementPtr getOptionDataList(const std::string &xpath)
Get and translate option data list from YANG to JSON.
isc::data::ElementPtr getItem(const std::string &xpath)
Get and translate basic value from YANG to JSON.
Definition: translator.cc:104
A translator class for converting a pool between YANG and JSON.
void setPoolIetf6(const std::string &xpath, isc::data::ConstElementPtr elem)
setPool for ietf-dhcpv6-server.
isc::data::ElementPtr getPools(const std::string &xpath)
Get and translate pools from YANG to JSON.
Option data translation between YANG and JSON.
void setOptionDataList(const std::string &xpath, isc::data::ConstElementPtr elem)
Translate and set option data list from JSON to YANG.
void setPoolsByAddresses(const std::string &xpath, isc::data::ConstElementPtr elem)
setPools using address pair.
virtual ~TranslatorPool()
Destructor.
isc::data::ElementPtr getPoolKea(const std::string &xpath)
getPool for kea-dhcp[46]-server.