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.7 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
311
			} else if (boost::iequals(val.first, "subSampling")) {
				sBase.setSubsampling(std::stoi(val.second.data()));
			} else if (boost::iequals(val.first, "publish")) {
312
313
				sBase.setPublish(to_bool(val.second.data()));
			} else if (boost::iequals(val.first, "metadata")) {
314
				SensorMetadata sm;
Alessio Netti's avatar
Alessio Netti committed
315
316
				if(sBase.getMetadata())
					sm = *sBase.getMetadata();
317
318
319
				try {
					sm.parsePTREE(val.second);
					sBase.setMetadata(sm);
320
				} catch (const std::exception &e) {
321
322
323
					LOG(error) << "  Metadata parsing failed for sensor " << sBase.getName() << "!" << std::endl;
				}
			}
324
		}
325
326
327
		if (sBase.getMqtt().size() == 0) {
			sBase.setMqtt(sBase.getName());
		}
328
329
330
		sensorBase(sBase, config);
		return true;
	}
331

332
	/**
333
334
335
336
337
338
339
340
341
342
343
344
     * @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
     */
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
	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.";
				}
			}
		}
363

364
365
366
367
		//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()));
368
369
			} else if (boost::iequals(val.first, "queueSize")) {
				sGroup.setQueueSize(stoull(val.second.data()));
370
371
372
373
			} 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());
374
375
376
377
378
			} 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)) {
379
380
381
				if (!isTemplate) {
					LOG(debug) << "  " << _baseName << " " << val.second.data();
				}
382

383
				SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
384
385
386
387
388
389
390
391
392
393
394
395
				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;
					}
				}
396
				if (readSensorBase(*sensor, val.second, false)) {
397
398
399
					if (!overwriting) {
						sGroup.pushBackSensor(sensor);
					}
400
401
402
403
404
405
				} else if (!isTemplate) {
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::"
						     << sensor->getName() << " could not be read! Omitting";
				}
			}
		}
406

407
408
409
410
		sensorGroup(sGroup, config);
		return true;
	}
	///@}
411

412
413
414
	///@name Overwrite in plugin
	///@{
	/**
415
416
417
418
419
     * 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
     */
420
	virtual void sensorBase(SBase &s, CFG_VAL config) = 0;
421

422
	/**
423
424
425
426
427
     * 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
     */
428
429
	virtual void sensorGroup(SGroup &s, CFG_VAL config) = 0;
	///@}
430

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

446
	/**
447
448
449
450
     * @brief Adjusts the names of the sensors in generated groups.
     *
     * @return True if successful, false otherwise.
     */
451
	virtual bool constructSensorTopics() {
452
453
454
455
456
457
458
459
460
		// 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) {
461
462
463
464
465
					sm->setPublicName(s->getMqtt());
					sm->setPattern(s->getMqtt());
					sm->setIsVirtual(false);
					if(!sm->getInterval())
						sm->setInterval((uint64_t)g->getInterval() * 1000000);
466
					sm->setDelta(s->getDelta());
467
468
469
470
471
472
473
				}
			}
			g->releaseSensors();
		}
		return true;
	}
	///@}
474

475
476
	std::string _groupName;
	std::string _baseName;
477

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

483
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */