2.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

ConfiguratorTemplate.h 16.8 KB
Newer Older
1
2
//================================================================================
// Name        : ConfiguratorTemplate.h
3
// Author      : Micha Mueller, Carla Guillen
Micha Müller's avatar
Micha Müller committed
4
// Contact     : info@dcdb.it
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Copyright   : Leibniz Supercomputing Centre
// Description : Interface template for plugin configurators.
//================================================================================

//================================================================================
// This file is part of DCDB (DataCenter DataBase)
// Copyright (C) 2018-2019 Leibniz Supercomputing Centre
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//================================================================================
27

28
29
#ifndef SRC_CONFIGURATORTEMPLATE_H_
#define SRC_CONFIGURATORTEMPLATE_H_
30

31
#include <map>
Alessio Netti's avatar
Alessio Netti committed
32
#include <set>
33

34
#include "ConfiguratorInterface.h"
35
36
37
38
39
40
#include "SensorGroupTemplate.h"
#include "sensorbase.h"
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/property_tree/ptree.hpp>
41

42
#include <algorithm>
43
#include <iomanip>
Alessio Netti's avatar
Alessio Netti committed
44
45
46
#include <iostream>
#include <sstream>

Micha Mueller's avatar
Micha Mueller committed
47
/**
48
 * @brief Interface template for plugin configurator implementations without
49
 *        entities.
50
 *
51
52
 * @details There is a derived template class for plugins with entities, see
 *          ConfiguratorTemplateEntity.
53
54
55
56
 *
 * @ingroup pusherplugins
 */
template <class SBase, class SGroup>
57
class ConfiguratorTemplate : public ConfiguratorInterface {
58
59
60
	//the template shall only be instantiated for classes which derive from SensorBase/SensorGroup
	static_assert(std::is_base_of<SensorBase, SBase>::value, "SBase must derive from SensorBase!");
	static_assert(std::is_base_of<SensorGroupInterface, SGroup>::value, "SGroup must derive from SensorGroupInterface!");
61

62
63
64
65
      protected:
	//TODO use smart pointers
	typedef std::map<std::string, SBase *>  sBaseMap_t;
	typedef std::map<std::string, SGroup *> sGroupMap_t;
66

67
68
	using SB_Ptr = std::shared_ptr<SBase>;
	using SG_Ptr = std::shared_ptr<SGroup>;
69

70
71
72
73
74
      public:
	ConfiguratorTemplate()
	    : ConfiguratorInterface(),
	      _groupName("INVALID"),
	      _baseName("INVALID") {}
75

76
77
78
79
80
81
82
83
84
85
86
	virtual ~ConfiguratorTemplate() {
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
		for (auto tg : _templateSensorGroups) {
			delete tg.second;
		}
		_sensorGroups.clear();
		_templateSensorBases.clear();
		_templateSensorGroups.clear();
	}
87

88
	/**
89
90
91
92
93
94
95
96
     * @brief Read in the given configuration
     *
     * @details Overwriting this method is only required if a custom logic is really necessary!
     *
     * @param   cfgPath Path to the config-file
     *
     * @return  True on success, false otherwise
     */
97
	bool readConfig(std::string cfgPath) override {
98
		_cfgPath = cfgPath;
99

100
101
		boost::property_tree::iptree cfg;
		boost::property_tree::read_info(cfgPath, cfg);
102

103
104
		//read global variables (if present overwrite those from global.conf)
		readGlobal(cfg);
105

106
		//read groups and templates for groups. If present also template stuff
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, cfg) {
			//template group
			if (boost::iequals(val.first, "template_" + _groupName)) {
				LOG(debug) << "Template " << _groupName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SGroup *group = new SGroup(val.second.data());
					if (readSensorGroup(*group, val.second, true)) {
						auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup *>(val.second.data(), group));
						if (!ret.second) {
							LOG(warning) << "Template " << _groupName << " "
								     << val.second.data() << " already exists! Omitting...";
							delete group;
						}
					} else {
						LOG(warning) << "Template " << _groupName << " \""
							     << val.second.data() << "\" has bad values! Ignoring...";
						delete group;
					}
				}
				//template base
			} else if (boost::iequals(val.first, "template_" + _baseName)) {
				LOG(debug) << "Template " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SBase *base = new SBase(val.second.data());
					if (readSensorBase(*base, val.second, true)) {
						auto ret = _templateSensorBases.insert(std::pair<std::string, SBase *>(val.second.data(), base));
						if (!ret.second) {
							LOG(warning) << "Template " << _baseName << " "
								     << val.second.data() << " already exists! Omitting...";
							delete base;
						}
					} else {
						LOG(warning) << "Template " << _baseName << " \""
							     << val.second.data() << "\" has bad values! Ignoring...";
						delete base;
					}
				}
				//template single sensor
			} else if (boost::iequals(val.first, "template_single_" + _baseName)) {
				LOG(debug) << "Template single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SGroup *group = new SGroup(val.second.data());
					if (readSensorGroup(*group, val.second, true)) {
						//group which consists of only one sensor
						SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
						if (readSensorBase(*sensor, val.second, true)) {
							group->pushBackSensor(sensor);
							auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup *>(val.second.data(), group));
							if (!ret.second) {
								LOG(warning) << "Template single " << _baseName << " "
									     << val.second.data() << " already exists! Omitting...";
								delete group;
							}
						} else {
							LOG(warning) << "Template single " << _baseName << " "
								     << val.second.data() << " could not be read! Omitting";
							delete group;
						}
					} else {
						LOG(warning) << "Template single " << _baseName << " \""
							     << val.second.data() << "\" has bad values! Ignoring...";
						delete group;
					}
				}
				//group
			} else if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << _groupName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SG_Ptr group = std::make_shared<SGroup>(val.second.data());
					if (readSensorGroup(*group, val.second)) {
						storeSensorGroup(group);
					} else {
						LOG(warning) << _groupName << " \""
							     << val.second.data() << "\" has bad values! Ignoring...";
					}
				}
				//single sensor
			} else if (boost::iequals(val.first, "single_" + _baseName)) {
				LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SG_Ptr group = std::make_shared<SGroup>(val.second.data());
					if (readSensorGroup(*group, val.second)) {
						//group which consists of only one sensor
						SB_Ptr sensor;
						//perhaps one sensor is already present because it was copied from the template group
192
193
194
195
196
						if (group->getDerivedSensors().size() != 0) {
							sensor = group->getDerivedSensors()[0];
							sensor->setName(val.second.data());
							if (readSensorBase(*sensor, val.second)) {
								storeSensorGroup(group);
197
198
							} else {
								LOG(warning) << "Single " << _baseName << " "
199
									     << val.second.data() << " could not be read! Omitting";
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
							}
						} else {
							sensor = std::make_shared<SBase>(val.second.data());
							if (readSensorBase(*sensor, val.second)) {
								group->pushBackSensor(sensor);
								storeSensorGroup(group);
							} else {
								LOG(warning) << "Single " << _baseName << " "
									     << val.second.data() << " could not be read! Omitting";
							}
						}
					} else {
						LOG(warning) << "Single " << _baseName << " \""
							     << val.second.data() << "\" has bad values! Ignoring...";
					}
				}
			} else if (!boost::iequals(val.first, "global")) {
				LOG(error) << "\"" << val.first << "\": unknown construct!";
				return false;
			}
		}
		//read of config finished. Now we build the mqtt-topic for every sensor
		return constructSensorTopics();
	}
224

225
	/**
226
227
     * @brief Clear internal storage and return plugin in unconfigured state.
     */
228
	void clearConfig() override {
229
230
231
232
		ConfiguratorInterface::clearConfig();
		// Bring everything to a halt
		for (auto g : _sensorGroups)
			g->stop();
233
		// Wait for stop
234
		for (auto g : _sensorGroups)
235
			g->wait();
236

237
238
239
240
241
242
243
244
245
246
247
		//clean up sensors/groups and templates
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
		for (auto tg : _templateSensorGroups) {
			delete tg.second;
		}
		_sensorGroups.clear();
		_templateSensorBases.clear();
		_templateSensorGroups.clear();
	}
248

249
	/**
250
251
252
253
     * @brief Print configuration.
     *
     * @param ll Log severity level to be used from logger.
     */
254
	void printConfig(LOG_LEVEL ll) override {
255
		ConfiguratorInterface::printConfig(ll);
256

257
258
		//prints plugin specific configurator attributes and entities if present
		printConfiguratorConfig(ll);
259

260
261
262
263
264
		LOG_VAR(ll) << "    " << _groupName << "s:";
		for (auto g : _sensorGroups) {
			g->printConfig(ll);
		}
	}
265

266
267
268
269
      protected:
	///@name Template-internal use only
	///@{
	/**
270
271
272
273
274
275
276
277
278
279
280
281
     * @brief Read common values of a sensorbase.
     *
     * @details Reads and sets the common base values of a sensor base
     *          (currently none), then calls sensorBase() to read plugin
     *          specific values.
     *
     * @param sBase      The sensor base for which to set the values.
     * @param config     A boost property (sub-)tree containing the values.
     * @param isTemplate Are we parsing a template or a regular sensor?
     *
     * @return  True on success, false otherwise
     */
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
	bool readSensorBase(SBase &sBase, CFG_VAL config, bool isTemplate = false) {
		sBase.setCacheInterval(_cacheInterval);
		if (!isTemplate) {
			boost::optional<boost::property_tree::iptree &> def = config.get_child_optional("default");
			if (def) {
				//we copy all values from default
				LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
				auto it = _templateSensorBases.find(def.get().data());
				if (it != _templateSensorBases.end()) {
					sBase = *(it->second);
					sBase.setName(config.data());
				} else {
					LOG(warning) << "Template " << _baseName << "\" "
						     << def.get().data() << "\" not found! Using standard values.";
				}
			}
		}
299

300
301
302
303
304
305
306
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, "mqttsuffix")) {
				sBase.setMqtt(val.second.data());
			} else if (boost::iequals(val.first, "skipConstVal")) {
				sBase.setSkipConstVal(to_bool(val.second.data()));
			} else if (boost::iequals(val.first, "delta")) {
				sBase.setDelta(to_bool(val.second.data()));
307
308
			} else if (boost::iequals(val.first, "deltaMax")) {
				sBase.setDeltaMaxValue(std::stoull(val.second.data()));
309
310
			} else if (boost::iequals(val.first, "subSampling")) {
				sBase.setSubsampling(std::stoi(val.second.data()));
311
312
			} else if (boost::iequals(val.first, "factor")) {
				sBase.setFactor(std::stod(val.second.data()));
313
			} else if (boost::iequals(val.first, "publish")) {
314
315
				sBase.setPublish(to_bool(val.second.data()));
			} else if (boost::iequals(val.first, "metadata")) {
316
				SensorMetadata sm;
Alessio Netti's avatar
Alessio Netti committed
317
318
				if(sBase.getMetadata())
					sm = *sBase.getMetadata();
319
320
321
				try {
					sm.parsePTREE(val.second);
					sBase.setMetadata(sm);
322
				} catch (const std::exception &e) {
323
324
325
					LOG(error) << "  Metadata parsing failed for sensor " << sBase.getName() << "!" << std::endl;
				}
			}
326
		}
327
328
329
		if (sBase.getMqtt().size() == 0) {
			sBase.setMqtt(sBase.getName());
		}
330
331
332
		sensorBase(sBase, config);
		return true;
	}
333

334
	/**
335
336
337
338
339
340
341
342
343
344
345
346
     * @brief Read common values of a sensorGroup.
     *
     * @details Reads and sets the common base values of a sensor group
     *          (currently none), then calls sensorGroup() to read plugin
     *          specific values.
     *
     * @param sGroup     The sensor group for which to set the values.
     * @param config     A boost property (sub-)tree containing the values.
     * @param isTemplate Are we parsing a template or a regular group?
     *
     * @return  True on success, false otherwise
     */
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
	bool readSensorGroup(SGroup &sGroup, CFG_VAL config, bool isTemplate = false) {
		//first check if default group is given, unless we are reading a template already
		if (!isTemplate) {
			boost::optional<boost::property_tree::iptree &> def = config.get_child_optional("default");
			if (def) {
				//we copy all values from default (including copy constructing its sensors)
				//if own sensors are specified they are appended
				LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
				auto it = _templateSensorGroups.find(def.get().data());
				if (it != _templateSensorGroups.end()) {
					sGroup = *(it->second);
					sGroup.setGroupName(config.data());
				} else {
					LOG(warning) << "Template " << _groupName << "\" "
						     << def.get().data() << "\" not found! Using standard values.";
				}
			}
		}
365

366
367
368
369
		//read in values inherited from SensorGroupInterface
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, "interval")) {
				sGroup.setInterval(stoull(val.second.data()));
370
371
			} else if (boost::iequals(val.first, "queueSize")) {
				sGroup.setQueueSize(stoull(val.second.data()));
372
373
374
375
			} else if (boost::iequals(val.first, "minValues")) {
				sGroup.setMinValues(stoull(val.second.data()));
			} else if (boost::iequals(val.first, "mqttPart")) {
				sGroup.setMqttPart(val.second.data());
376
377
378
379
380
			} else if (boost::iequals(val.first, "sync")) {
				sGroup.setSync(to_bool(val.second.data()));
			} else if (boost::iequals(val.first, "disabled")) {
				sGroup.setDisabled(to_bool(val.second.data()));
			} else if (boost::iequals(val.first, _baseName)) {
381
382
383
				if (!isTemplate) {
					LOG(debug) << "  " << _baseName << " " << val.second.data();
				}
384

385
				SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
386
387
388
389
390
391
392
393
394
395
396
397
				bool   overwriting = false;

				//Check if sensor with equal name is already present.
				//If yes, we do not want a new sensor but instead overwrite the
				//existing one.
				for (const auto &s : sGroup.getDerivedSensors()) {
					if (s->getName() == sensor->getName()) {
						sensor = s;
						overwriting = true;
						break;
					}
				}
398
				if (readSensorBase(*sensor, val.second, false)) {
399
400
401
					if (!overwriting) {
						sGroup.pushBackSensor(sensor);
					}
402
403
404
405
406
407
				} else if (!isTemplate) {
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::"
						     << sensor->getName() << " could not be read! Omitting";
				}
			}
		}
408

409
410
411
412
		sensorGroup(sGroup, config);
		return true;
	}
	///@}
413

414
415
416
	///@name Overwrite in plugin
	///@{
	/**
417
418
419
420
421
     * Method responsible for reading plugin-specific sensor base values.
     *
     * @param s         The sensor base for which to set the values
     * @param config    A boost property (sub-)tree containing the sensor values
     */
422
	virtual void sensorBase(SBase &s, CFG_VAL config) = 0;
423

424
	/**
425
426
427
428
429
     * Method responsible for reading plugin-specific sensor group values.
     *
     * @param s         The sensor group for which to set the values
     * @param config    A boost property (sub-)tree containing the group values
     */
430
431
	virtual void sensorGroup(SGroup &s, CFG_VAL config) = 0;
	///@}
432

433
434
435
	///@name Utility
	///@{
	/**
436
437
     * @brief Store a sensor group internally.
     *
438
439
440
     * @details If a group with identical name is already present, it will be
     *          replaced.
     *
441
442
     * @param sGroup Group to store.
     */
443
444
445
446
	void storeSensorGroup(SG_Ptr sGroup) {
		_sensorGroups.push_back(sGroup);
		_sensorGroupInterfaces.push_back(sGroup);
	}
447

448
	/**
449
450
451
452
     * @brief Adjusts the names of the sensors in generated groups.
     *
     * @return True if successful, false otherwise.
     */
453
	virtual bool constructSensorTopics() {
454
455
456
457
458
459
460
461
462
		// Sensor names are adjusted according to the respective MQTT topics
		for (auto &g : _sensorGroups) {
			for (auto &s : g->acquireSensors()) {
				s->setMqtt(MQTTChecker::formatTopic(_mqttPrefix) +
					   MQTTChecker::formatTopic(g->getMqttPart()) +
					   MQTTChecker::formatTopic(s->getMqtt()));
				s->setName(s->getMqtt());
				SensorMetadata *sm = s->getMetadata();
				if (sm) {
463
464
465
466
467
					sm->setPublicName(s->getMqtt());
					sm->setPattern(s->getMqtt());
					sm->setIsVirtual(false);
					if(!sm->getInterval())
						sm->setInterval((uint64_t)g->getInterval() * 1000000);
468
					sm->setDelta(s->getDelta());
469
470
471
472
473
474
475
				}
			}
			g->releaseSensors();
		}
		return true;
	}
	///@}
476

477
478
	std::string _groupName;
	std::string _baseName;
479

480
481
482
	std::vector<SG_Ptr> _sensorGroups;
	sBaseMap_t          _templateSensorBases;
	sGroupMap_t         _templateSensorGroups;
483
484
};

485
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */