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

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

362
363
364
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()));
			} 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());
370
371
372
373
374
			} 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)) {
375
376
377
				if (!isTemplate) {
					LOG(debug) << "  " << _baseName << " " << val.second.data();
				}
378

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

403
404
405
406
		sensorGroup(sGroup, config);
		return true;
	}
	///@}
407

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

418
	/**
419
420
421
422
423
     * 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
     */
424
425
	virtual void sensorGroup(SGroup &s, CFG_VAL config) = 0;
	///@}
426

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

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

470
471
	std::string _groupName;
	std::string _baseName;
472

473
474
475
	std::vector<SG_Ptr> _sensorGroups;
	sBaseMap_t          _templateSensorBases;
	sGroupMap_t         _templateSensorGroups;
476
477
};

478
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */