EMANE  1.2.1
configurationservice.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013,2015-2016 - Adjacent Link LLC, Bridgewater, New
3  * Jersey
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of Adjacent Link LLC nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "configurationservice.h"
36 
37 #include <pcre.h>
38 
40 
42  RunningStateMutable * pRunningStateMutable)
43 {
44  std::lock_guard<std::mutex> m(mutex_);
45 
46  runningStateMutables_.insert(std::make_pair(buildId,pRunningStateMutable));
47 }
48 
50  const std::string & sName,
51  Any::Type type,
52  const ConfigurationProperties & properties,
53  const std::vector<Any> & values,
54  const std::string & sUsage,
55  const Any & minValue,
56  const Any & maxValue,
57  std::size_t minOccurs,
58  std::size_t maxOccurs,
59  const std::string & sRegexPattern)
60 {
61  registerAny(buildId,
62  sName,
63  ConfigurationInfo{sName,
64  type,
65  properties,
66  values,
67  sUsage,
68  minValue,
69  maxValue,
70  minOccurs,
71  maxOccurs,
72  sRegexPattern});
73 }
74 
76  const std::string & sName,
77  Any::Type type,
78  const ConfigurationProperties & properties,
79  const std::vector<Any> & values,
80  const std::string & sUsage,
81  std::size_t minOccurs,
82  std::size_t maxOccurs,
83  const std::string & sRegexPattern)
84 {
85  registerAny(buildId,
86  sName,
87  ConfigurationInfo{sName,
88  type,
89  properties,
90  values,
91  sUsage,
92  minOccurs,
93  maxOccurs,
94  sRegexPattern});
95 }
96 
97 void EMANE::ConfigurationService::registerAny(BuildId buildId,
98  const std::string & sName,
99  ConfigurationInfo && configurationInfo)
100 {
101  std::lock_guard<std::mutex> m(mutex_);
102 
103  if(std::find_if_not(sName.begin(),sName.end(),[](int ch){return isalnum(ch) || ch == '.';}) != sName.end())
104  {
105  throw makeException<RegistrarException>("Invalid charater in the configuration name: %s",
106  sName.c_str());
107  }
108 
109  // check the regex is valid if defined
110  if(!configurationInfo.getRegexPattern().empty())
111  {
112  pcre * pPCRE{};
113  const char * pError{};
114  int iErrorOffset{};
115 
116  pPCRE = pcre_compile(configurationInfo.getRegexPattern().c_str(),
117  0,
118  &pError,
119  &iErrorOffset,
120  0);
121 
122  if(!pPCRE)
123  {
124  throw makeException<RegistrarException>("Bad regex pattern defined for %s: %s (offset:%i) %s",
125  sName.c_str(),
126  configurationInfo.getRegexPattern().c_str(),
127  iErrorOffset,
128  pError);
129  }
130  else
131  {
132  pcre_free(pPCRE);
133  }
134 
135  }
136 
137  auto iter = buildIdConfigurationStore_.find(buildId);
138 
139  if(iter != buildIdConfigurationStore_.end())
140  {
141  if(!iter->second.insert(std::make_pair(sName,configurationInfo)).second)
142  {
143  throw makeException<RegistrarException>("Duplicate configuration name registration detected: %s",
144  sName.c_str());
145  }
146  }
147  else
148  {
149  ConfigurationStore store;
150 
151  store.insert(std::make_pair(sName,configurationInfo));
152 
153  buildIdConfigurationStore_.insert(std::make_pair(buildId,std::move(store)));
154  }
155 }
156 
157 
158 
161 {
162  std::lock_guard<std::mutex> m(mutex_);
163 
164  ConfigurationManifest infos;
165 
166  auto iter = buildIdConfigurationStore_.find(buildId);
167 
168  if(iter != buildIdConfigurationStore_.end())
169  {
170  auto & store = iter->second;
171 
172  std::transform(store.begin(),
173  store.end(),
174  std::back_inserter(infos),
175  std::bind(&ConfigurationStore::value_type::second,std::placeholders::_1));
176  }
177 
178  return infos;
179 }
180 
181 std::vector<std::pair<std::string,std::vector<EMANE::Any>>>
183  const std::vector<std::string> & names) const
184 {
185  std::lock_guard<std::mutex> m(mutex_);
186 
187  std::vector<std::pair<std::string,std::vector<Any>>> values;
188 
189  auto iter = buildIdConfigurationStore_.find(buildId);
190 
191  if(iter != buildIdConfigurationStore_.end())
192  {
193  auto & store = iter->second;
194 
195  if(names.empty())
196  {
197  for_each(store.begin(),
198  store.end(),
199  bind([&values](const ConfigurationInfo & info)
200  {
201  values.push_back(std::make_pair(info.getName(),info.getValues()));
202  },
203  bind(&ConfigurationStore::value_type::second,
204  std::placeholders::_1)));
205  }
206  else
207  {
208  for_each(names.begin(),
209  names.end(),
210  [&store,&values](const std::string & s)
211  {
212  auto iter = store.find(s);
213 
214  if(iter != store.end())
215  {
216  values.push_back(std::make_pair(s,iter->second.getValues()));
217  }
218  else
219  {
220  throw makeException<RegistrarException>("Unknown configuration parameter: %s",
221  s.c_str());
222  }
223  });
224  }
225  }
226 
227  return values;
228 }
229 
230 
233  const ConfigurationUpdateRequest & parameters)
234 {
235  std::lock_guard<std::mutex> m(mutex_);
236 
237  ConfigurationUpdate updates;
238 
239  auto iter = buildIdConfigurationStore_.find(buildId);
240 
241  if(iter != buildIdConfigurationStore_.end())
242  {
243  auto & store = iter->second;
244 
245  for(const auto & paramIter : parameters)
246  {
247  auto infoIter = store.find(paramIter.first);
248 
249  if(infoIter != store.end())
250  {
251  if(paramIter.second.size() >= infoIter->second.getMinOccurs() &&
252  paramIter.second.size() <= infoIter->second.getMaxOccurs())
253  {
254  pcre * pPCRE{};
255  const char * pError{};
256  int iErrorOffset{};
257 
258  // if regex defined, verify a match
259  const std::string & sRegexPattern = infoIter->second.getRegexPattern();
260 
261  if(!sRegexPattern.empty())
262  {
263  pPCRE = pcre_compile(sRegexPattern.c_str(),
264  0,
265  &pError,
266  &iErrorOffset,
267  0);
268  }
269 
270  std::vector<Any> anys;
271 
272  for(const auto & value : paramIter.second)
273  {
274  try
275  {
276  auto anyType = infoIter->second.getType();
277 
278  Any any{Any::create(value,anyType)};
279 
280  // if numeric any verify range
281  if(anyType != Any::Type::TYPE_INET_ADDR &&
282  anyType != Any::Type::TYPE_STRING)
283  {
284  if(any < infoIter->second.getMinValue() ||
285  any > infoIter->second.getMaxValue())
286  {
287  throw makeException<ConfigurationException>("Out of range %s set to %s [%s,%s]",
288  paramIter.first.c_str(),
289  any.toString().c_str(),
290  infoIter->second.getMinValue().toString().c_str(),
291  infoIter->second.getMaxValue().toString().c_str());
292  }
293  }
294 
295  if(pPCRE)
296  {
297  if(pcre_exec(pPCRE,
298  nullptr,
299  value.c_str(),
300  value.size(),
301  0,
302  0,
303  0,
304  0) < 0)
305  {
306  throw makeException<ConfigurationException>("Regular expression mismatch %s set to %s (%s)",
307  paramIter.first.c_str(),
308  value.c_str(),
309  sRegexPattern.c_str());
310  }
311  }
312 
313  anys.push_back(any);
314  }
315  catch(AnyException & exp)
316  {
317  throw makeException<ConfigurationException>("%s Parameter %s set to %s",
318  exp.what(),
319  paramIter.first.c_str(),
320  value.c_str());
321  }
322  }
323 
324  updates.push_back(std::make_pair(paramIter.first,anys));
325 
326  if(pPCRE)
327  {
328  pcre_free(pPCRE);
329  }
330  }
331  else
332  {
333  throw makeException<ConfigurationException>("Value occurrence out of range %s has %zu values [%zu,%zu]",
334  paramIter.first.c_str(),
335  paramIter.second.size(),
336  infoIter->second.getMinOccurs(),
337  infoIter->second.getMaxOccurs());
338 
339  }
340  }
341  else
342  {
343  throw makeException<ConfigurationException>("Parameter not registered %s",
344  paramIter.first.c_str());
345  }
346  }
347 
348  // determine if any missing item is required or has a default
349  for(const auto & iter : store)
350  {
351  const auto & sParamName(iter.first);
352  const auto & item(iter.second);
353 
354  if(item.isRequired() || item.hasDefault())
355  {
356  if(std::find_if(updates.begin(),
357  updates.end(),
358  std::bind(std::equal_to<std::string>(),
359  std::bind(&ConfigurationUpdate::value_type::first,
360  std::placeholders::_1),
361  sParamName)) == updates.end())
362  {
363  if(item.isRequired())
364  {
365  throw makeException<ConfigurationException>("Required item not present: %s",
366  sParamName.c_str());
367  }
368  else
369  {
370  // item has a defualt value, so use it
371  updates.push_back(std::make_pair(sParamName,item.getValues()));
372  }
373  }
374  }
375  }
376 
377 
378  // run any registered validators
379  auto validatorIter = validatorStore_.find(buildId);
380 
381  if(validatorIter != validatorStore_.end())
382  {
383  for(auto validator : validatorIter->second)
384  {
385  auto ret = validator(updates);
386 
387  if(!ret.second)
388  {
389  throw makeException<ConfigurationException>("Validator failure: %s",ret.first.c_str());
390  }
391  }
392  }
393 
394 
395  // update cache of current configuration values - we know the parameter
396  // is present, we would have thrown an
397  std::for_each(updates.begin(),
398  updates.end(),
399  [&store](const ConfigurationUpdate::value_type & v)
400  {
401  store.find(v.first)->second.setValues(v.second);
402  });
403 
404  }
405  else
406  {
407  if(!parameters.empty())
408  {
409  std::string sUnexpected{};
410 
411  for(const auto & parameter : parameters)
412  {
413  sUnexpected.append(parameter.first + " ");
414  }
415 
416  throw makeException<ConfigurationException>("Parameter(s) not registered: %s",
417  sUnexpected.c_str());
418  }
419  }
420  return updates;
421 }
422 
424  const ConfigurationUpdate & updates)
425 {
426  std::lock_guard<std::mutex> m(mutex_);
427 
428  auto iter = buildIdConfigurationStore_.find(buildId);
429 
430  if(iter != buildIdConfigurationStore_.end())
431  {
432  auto & store = iter->second;
433 
434  for(const auto & update : updates)
435  {
436  auto infoIter = store.find(update.first);
437 
438  if(infoIter != store.end())
439  {
440  // verify parameter is allowed to be updated
441  if(!infoIter->second.isModifiable())
442  {
443  throw makeException<ConfigurationException>("Parameter not running state modifiable %s",
444  update.first.c_str());
445  }
446 
447 
448  if(update.second.size() >= infoIter->second.getMinOccurs() &&
449  update.second.size() <= infoIter->second.getMaxOccurs())
450  {
451  pcre * pPCRE{};
452  const char * pError{};
453  int iErrorOffset{};
454 
455  // if regex defined, verify a match
456  const std::string & sRegexPattern = infoIter->second.getRegexPattern();
457 
458  if(!sRegexPattern.empty())
459  {
460  pPCRE = pcre_compile(sRegexPattern.c_str(),
461  0,
462  &pError,
463  &iErrorOffset,
464  0);
465  }
466 
467  std::vector<Any> anys;
468 
469  for(const auto & value : update.second)
470  {
471  auto anyType = infoIter->second.getType();
472 
473  if(anyType!= value.getType())
474  {
475  throw makeException<ConfigurationException>("Parameter value type incorrect %s",
476  update.first.c_str());
477  }
478 
479  // if numeric any verify range
480  if(anyType != Any::Type::TYPE_INET_ADDR &&
481  anyType != Any::Type::TYPE_STRING)
482  {
483  if(value < infoIter->second.getMinValue() ||
484  value > infoIter->second.getMaxValue())
485  {
486  throw makeException<ConfigurationException>("Out of range %s set to %s [%s,%s]",
487  update.first.c_str(),
488  value.toString().c_str(),
489  infoIter->second.getMinValue().toString().c_str(),
490  infoIter->second.getMaxValue().toString().c_str());
491  }
492  }
493 
494  if(pPCRE)
495  {
496  std::string sValue{value.toString()};
497 
498  if(pcre_exec(pPCRE,
499  nullptr,
500  sValue.c_str(),
501  sValue.size(),
502  0,
503  0,
504  0,
505  0) < 0)
506  {
507  throw makeException<ConfigurationException>("Regular expression mismatch %s set to %s (%s)",
508  update.first.c_str(),
509  sValue.c_str(),
510  sRegexPattern.c_str());
511  }
512  }
513 
514  }
515 
516  if(pPCRE)
517  {
518  pcre_free(pPCRE);
519  }
520  }
521  else
522  {
523  throw makeException<ConfigurationException>("Value occurrence out of range %s has %zu values [%zu,%zu]",
524  update.first.c_str(),
525  update.second.size(),
526  infoIter->second.getMinOccurs(),
527  infoIter->second.getMaxOccurs());
528 
529  }
530  }
531  else
532  {
533  throw makeException<ConfigurationException>("Parameter not registered %s",
534  update.first.c_str());
535  }
536  }
537 
538 
539  auto validatorIter = validatorStore_.find(buildId);
540 
541  if(validatorIter != validatorStore_.end())
542  {
543  auto canidateUpdates = updates;
544 
545  for(const auto & iter : store)
546  {
547  const auto & sParamName(iter.first);
548  const auto & item(iter.second);
549 
550  const auto & values = item.getValues();
551 
552  if(!values.empty())
553  {
554  if(std::find_if(canidateUpdates.begin(),
555  canidateUpdates.end(),
556  std::bind(std::equal_to<std::string>(),
557  std::bind(&ConfigurationUpdate::value_type::first,
558  std::placeholders::_1),
559  sParamName)) == canidateUpdates.end())
560  {
561  // item has a defualt value, so use it
562  canidateUpdates.push_back(std::make_pair(sParamName,item.getValues()));
563  }
564  }
565  }
566 
567  for(auto validator : validatorIter->second)
568  {
569  auto ret = validator(canidateUpdates);
570 
571  if(!ret.second)
572  {
573  throw makeException<ConfigurationException>("Validator failure: %s",ret.first.c_str());
574  }
575  }
576  }
577 
578  auto mutablesIter = runningStateMutables_.find(buildId);
579 
580  if(mutablesIter != runningStateMutables_.end())
581  {
582  // update cache of current configuration values - we know the parameter
583  // is present, we would have thrown an exception
584  std::for_each(updates.begin(),
585  updates.end(),
586  [&store](const ConfigurationUpdate::value_type & v)
587  {
588  store.find(v.first)->second.setValues(v.second);
589  });
590 
591  mutablesIter->second->processConfiguration(updates);
592  }
593  else
594  {
595  throw makeException<ConfigurationException>("No component registered with build id %hu",buildId);
596  }
597 
598  }
599 }
600 
602 {
603  std::lock_guard<std::mutex> m(mutex_);
604 
605  auto iter = validatorStore_.find(buildId);
606 
607  if(iter != validatorStore_.end())
608  {
609  iter->second.push_back(validator);
610  }
611  else
612  {
613  validatorStore_.insert(std::make_pair(buildId,std::vector<ConfigurationValidator>{validator}));
614  }
615 }
void registerNonNumericAny(BuildId buildId, const std::string &sName, Any::Type type, const ConfigurationProperties &properties, const std::vector< Any > &values, const std::string &sUsage, std::size_t minOccurs, std::size_t maxOccurs, const std::string &sRegexPattern)
void registerNumericAny(BuildId buildId, const std::string &sName, Any::Type type, const ConfigurationProperties &properties, const std::vector< Any > &values, const std::string &sUsage, const Any &minValue, const Any &maxValue, std::size_t minOccurs, std::size_t maxOccurs, const std::string &sRegexPattern)
Type
Definition: any.h:52
const std::string & getName() const
std::vector< std::pair< std::string, std::vector< Any > > > queryConfiguration(BuildId buildId, const std::vector< std::string > &names={}) const
const std::vector< Any > & getValues() const
const char * what() const
Definition: exception.h:62
The RunningStateMutable interface is used to allow dynamic running-state configuration changes...
std::vector< ConfigurationInfo > ConfigurationManifest
void registerRunningStateMutable(BuildId buildId, RunningStateMutable *pRunningStateMutable)
void update(BuildId buildId, const ConfigurationUpdate &update)
void registerValidator(BuildId buildId, ConfigurationValidator validator)
ConfigurationUpdate buildUpdates(BuildId buildId, const ConfigurationUpdateRequest &parameters)
ConfigurationManifest getConfigurationManifest(BuildId buildId) const
std::vector< ConfigurationNameStringValues > ConfigurationUpdateRequest
std::vector< ConfigurationNameAnyValues > ConfigurationUpdate
std::function< std::pair< std::string, bool >(const ConfigurationUpdate &update) noexcept > ConfigurationValidator
static Any create(std::string sValue, Type type)
Definition: any.cc:343
Holds configuration item meta information.
AnyException is thrown when an exception occurs during creation or conversion of an Any...
Definition: anyexception.h:46
The Any class can contain an instance of one of any type in its support type set. ...
Definition: any.h:49
std::uint32_t BuildId
Definition: types.h:60