ConfiguratorTemplate.h 16.3 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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
		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
						if (group->acquireSensors().size() != 0) {
							group->releaseSensors();
							sensor = std::dynamic_pointer_cast<SBase>(group->acquireSensors()[0]);
							group->releaseSensors();
							//check if cast was successful (sensor != nullptr)
							if (sensor) {
								sensor->setName(val.second.data());
								if (readSensorBase(*sensor, val.second)) {
									storeSensorGroup(group);
								} else {
									LOG(warning) << "Single " << _baseName << " "
										     << val.second.data() << " could not be read! Omitting";
								}
							} else {
								LOG(warning) << "Single " << _baseName << " "
									     << val.second.data() << " had a type mismatch when casting! Omitting";
							}
						} else {
							group->releaseSensors();
							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();
	}
233

234
	/**
235
236
     * @brief Clear internal storage and return plugin in unconfigured state.
     */
237
	void clearConfig() override {
238
239
240
241
		ConfiguratorInterface::clearConfig();
		// Bring everything to a halt
		for (auto g : _sensorGroups)
			g->stop();
242
		// Wait for stop
243
		for (auto g : _sensorGroups)
244
			g->wait();
245

246
247
248
249
250
251
252
253
254
255
256
		//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();
	}
257

258
	/**
259
260
261
262
     * @brief Print configuration.
     *
     * @param ll Log severity level to be used from logger.
     */
263
	void printConfig(LOG_LEVEL ll) override {
264
		ConfiguratorInterface::printConfig(ll);
265

266
267
		//prints plugin specific configurator attributes and entities if present
		printConfiguratorConfig(ll);
268

269
270
271
272
273
		LOG_VAR(ll) << "    " << _groupName << "s:";
		for (auto g : _sensorGroups) {
			g->printConfig(ll);
		}
	}
274

275
276
277
278
      protected:
	///@name Template-internal use only
	///@{
	/**
279
280
281
282
283
284
285
286
287
288
289
290
     * @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
     */
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
	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.";
				}
			}
		}
308

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

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

368
369
370
371
372
373
374
375
		//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());
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
384
385
386
387
388
389
390
391
392
				if (!isTemplate) {
					LOG(debug) << "  " << _baseName << " " << val.second.data();
				}
				SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
				if (readSensorBase(*sensor, val.second, isTemplate)) {
					sGroup.pushBackSensor(sensor);
				} else if (!isTemplate) {
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::"
						     << sensor->getName() << " could not be read! Omitting";
				}
			}
		}
393

394
395
396
397
		sensorGroup(sGroup, config);
		return true;
	}
	///@}
398

399
400
401
	///@name Overwrite in plugin
	///@{
	/**
402
403
404
405
406
     * 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
     */
407
	virtual void sensorBase(SBase &s, CFG_VAL config) = 0;
408

409
	/**
410
411
412
413
414
     * 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
     */
415
416
	virtual void sensorGroup(SGroup &s, CFG_VAL config) = 0;
	///@}
417

418
419
420
	///@name Utility
	///@{
	/**
421
422
423
424
     * @brief Store a sensor group internally.
     *
     * @param sGroup Group to store.
     */
425
426
427
428
	void storeSensorGroup(SG_Ptr sGroup) {
		_sensorGroups.push_back(sGroup);
		_sensorGroupInterfaces.push_back(sGroup);
	}
429

430
	/**
431
432
433
434
     * @brief Adjusts the names of the sensors in generated groups.
     *
     * @return True if successful, false otherwise.
     */
435
	virtual bool constructSensorTopics() {
436
437
438
439
440
441
442
443
444
445
		// 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) {
					sm->publicName = s->getMqtt();
446
					sm->pattern = s->getMqtt();
447
					sm->isVirtual = false;
Alessio Netti's avatar
Alessio Netti committed
448
449
					if(sm->interval==0)
						sm->interval = (unsigned long long)g->getInterval() * 1000000;
450
451
452
453
454
455
456
				}
			}
			g->releaseSensors();
		}
		return true;
	}
	///@}
457

458
459
	std::string _groupName;
	std::string _baseName;
460

461
462
463
	std::vector<SG_Ptr> _sensorGroups;
	sBaseMap_t          _templateSensorBases;
	sGroupMap_t         _templateSensorGroups;
464
465
};

466
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */