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

ConfiguratorTemplateEntity.h 17.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//================================================================================
// Name        : ConfiguratorTemplateEntity.h
// Author      : Micha Mueller
// Contact     : info@dcdb.it
// Copyright   : Leibniz Supercomputing Centre
// Description : Interface template for plugin configurators with entities.
//================================================================================

//================================================================================
// This file is part of DCDB (DataCenter DataBase)
// Copyright (C) 2019-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.
//================================================================================

#ifndef DCDBPUSHER_INCLUDES_CONFIGURATORTEMPLATEENTITY_H_
#define DCDBPUSHER_INCLUDES_CONFIGURATORTEMPLATEENTITY_H_

#include "ConfiguratorTemplate.h"
#include "EntityInterface.h"

/**
 * @brief Interface template for plugin configurator implementations with
 *        entities.
 *
 * @details Inherits from the ConfiguratorTemplate class and extends it for
 *          entity support
 *
 * @ingroup pusherplugins
 */
template <class SBase, class SGroup, class SEntity>
class ConfiguratorTemplateEntity : public ConfiguratorTemplate<SBase, SGroup> {
	//the template shall only be instantiated for entities which derive from EntityInterface
	static_assert(std::is_base_of<EntityInterface, SEntity>::value, "SEntity must derive from EntityInterface!");

      protected:
	//mention all required parent attributes and functions here to avoid compiler errors
	using ConfiguratorInterface::_cfgPath;
	using ConfiguratorInterface::_mqttPrefix;
	using ConfiguratorInterface::readGlobal;
	using ConfiguratorTemplate<SBase, SGroup>::_baseName;
	using ConfiguratorTemplate<SBase, SGroup>::_groupName;
	using ConfiguratorTemplate<SBase, SGroup>::_sensorGroups;
	using ConfiguratorTemplate<SBase, SGroup>::_templateSensorBases;
	using ConfiguratorTemplate<SBase, SGroup>::_templateSensorGroups;
	using ConfiguratorTemplate<SBase, SGroup>::readSensorBase;
	using ConfiguratorTemplate<SBase, SGroup>::readSensorGroup;
	using ConfiguratorTemplate<SBase, SGroup>::storeSensorGroup;

	//TODO use smart pointers
	typedef std::map<std::string, SEntity *> sEntityMap_t;

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

      public:
	ConfiguratorTemplateEntity()
	    : ConfiguratorTemplate<SBase, SGroup>(),
	      _entityName("INVALID") {}

	virtual ~ConfiguratorTemplateEntity() {
		for (auto e : _sensorEntitys) {
			delete e;
		}
		for (auto te : _templateSensorEntitys) {
			delete te.second;
		}
		// SensorGroups must be cleared before Entities
		_sensorGroups.clear();
		_sensorEntitys.clear();
		_templateSensorEntitys.clear();
	}

	/**
     * @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
     */
	bool readConfig(std::string cfgPath) override {
		_cfgPath = cfgPath;

		boost::property_tree::iptree cfg;
		boost::property_tree::read_info(cfgPath, cfg);

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

104
		//read entities and template stuff
105
106
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, cfg) {
			//template entity
			if (boost::iequals(val.first, "template_" + _entityName)) {
				LOG(debug) << "Template " << _entityName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					//name of an entity is only used to identify templates and is not stored otherwise
					SEntity *entity = new SEntity(val.second.data());
					if (readSensorEntity(*entity, val.second, true)) {
						auto ret = _templateSensorEntitys.insert(std::pair<std::string, SEntity *>(val.second.data(), entity));
						if (!ret.second) {
							LOG(warning) << "Template " << _entityName << " "
								     << val.second.data() << " already exists! Omitting...";
							delete entity;
						}
					} else {
						LOG(warning) << "Template " << _entityName << " \""
							     << val.second.data() << "\" has bad values! Ignoring...";
						delete entity;
					}
				}
				//template group
			} else 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;
					}
				}
				//entity
			} else if (boost::iequals(val.first, _entityName)) {
				LOG(debug) << _entityName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SEntity *entity = new SEntity(val.second.data());
					if (readSensorEntity(*entity, val.second)) {
						_sensorEntitys.push_back(entity);
					} else {
						LOG(warning) << _entityName << " \""
							     << val.second.data() << "\" has bad values! Ignoring...";
						delete entity;
					}
				}
			} 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();
	}

	/**
     * @brief Clear internal storage and return plugin in unconfigured state.
     */
	void clearConfig() final override {
		ConfiguratorTemplate<SBase, SGroup>::clearConfig();

		//clean up remaining entity stuff
		for (auto e : _sensorEntitys) {
			delete e;
		}
		for (auto te : _templateSensorEntitys) {
			delete te.second;
		}
		_sensorEntitys.clear();
		_templateSensorEntitys.clear();
	}

	/**
     * @brief Print configuration.
     *
     * @param ll Log severity level to be used from logger.
     */
	void printConfig(LOG_LEVEL ll) final override {
		ConfiguratorInterface::printConfig(ll);

		//prints plugin specific configurator attributes and entities if present
		this->printConfiguratorConfig(ll);

		LOG_VAR(ll) << "    " << _entityName << "s:";
		if (_sensorEntitys.size() != 0) {
			for (auto e : _sensorEntitys) {
				e->printConfig(ll, 8);
				LOG_VAR(ll) << "            Sensor Groups:";
				for (auto g : _sensorGroups) {
					if (g->getEntity() == e) {
						g->printConfig(ll, 16);
					}
				}
			}
		} else {
			LOG_VAR(ll) << "            No " << _entityName << "s present!";
		}
	}

      protected:
	///@name Template-internal use only
	///@{
	/**
     * @brief Read common values of a sensor entity.
     *
     * @details Reads and sets the common base values of a sensor entity
     *          (currently none), then calls sensorEntity() to read plugin
     *          specific values.
     *
     * @param sEntity    The aggregating entity for which to set the values.
     * @param config     A boost property (sub-)tree containing the values.
     * @param isTemplate Indicate if sEntity is a template. If so, also store
     *                   the corresponding sGroups in the template map.
     *
     * @return  True on success, false otherwise
     */
	bool readSensorEntity(SEntity &sEntity, CFG_VAL config, bool isTemplate = false) {
		//first check if default entity is given
		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 = _templateSensorEntitys.find(def.get().data());
			if (it != _templateSensorEntitys.end()) {
				sEntity = *(it->second);
				sEntity.setName(config.data());
				for (auto g : _templateSensorGroups) {
					if (it->second == g.second->getEntity()) {
						SG_Ptr group = std::make_shared<SGroup>(*(g.second));
						group->setEntity(&sEntity);
						if (group->getGroupName().size() > 0) {
							group->setGroupName(sEntity.getName() + "::" + group->getGroupName());
						} else {
							group->setGroupName(sEntity.getName());
						}
						storeSensorGroup(group);
					}
				}
			} else {
				LOG(warning) << "Template " << _entityName << "\""
					     << def.get().data() << "\" not found! Using standard values.";
			}
		}

		//read in values inherited from EntityInterface
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, "mqttPart")) {
				sEntity.setMqttPart(val.second.data());
			} else if (boost::iequals(val.first, "disabled")) {
				sEntity.setDisabled(to_bool(val.second.data()));
			}
		}

		sensorEntity(sEntity, config);

		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << "  " << _groupName << " " << val.second.data();
				if (!val.second.empty()) {
					if (isTemplate) {
						SGroup *group = new SGroup(val.second.data());
						if (readSensorGroup(*group, val.second)) {
							group->setEntity(&sEntity);
							auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup *>(sEntity.getName() + "::" + group->getGroupName(), group));
							if (!ret.second) {
								LOG(warning) << "Template " << _groupName << " "
									     << sEntity.getName() + "::" + group->getGroupName() << " already exists! Omitting...";
								delete group;
							}
						} else {
							LOG(warning) << _groupName << " " << group->getGroupName()
								     << " could not be read! Omitting";
							delete group;
						}
					} else {
						SG_Ptr group = std::make_shared<SGroup>(val.second.data());
331
                        group->setEntity(&sEntity);
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
						bool   overwriting = false;

						if (group->getGroupName().size() > 0) {
							group->setGroupName(sEntity.getName() + "::" + group->getGroupName());
						} else {
							group->setGroupName(sEntity.getName());
						}

						//Check if group with equal name is already present.
						//If yes, we do not want a new group but instead
						//overwrite the existing one.
						for (const auto &g : _sensorGroups) {
							if ((g->getGroupName() == group->getGroupName()) && (&sEntity == g->getEntity())) {
								group = g;
								overwriting = true;
								break;
348
							}
349
350
351
						}

						if (readSensorGroup(*group, val.second) && !overwriting) {
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
							storeSensorGroup(group);
						} else {
							LOG(warning) << _groupName << " " << group->getGroupName()
								     << " could not be read! Omitting";
						}
					}
				}
			} else if (boost::iequals(val.first, "single_" + _baseName)) {
				LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					if (isTemplate) {
						SGroup *group = new SGroup(val.second.data());
						//group which consists of only one sensor
						if (readSensorGroup(*group, val.second)) {
							group->setEntity(&sEntity);
							SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
							if (readSensorBase(*sensor, val.second)) {
								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) << "Single " << _baseName << " \""
								     << val.second.data() << "\" has bad values! Ignoring...";
							delete group;
						}
					} else {
						SG_Ptr group = std::make_shared<SGroup>(val.second.data());
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
						bool   overwriting = false;

						if (group->getGroupName().size() > 0) {
							group->setGroupName(sEntity.getName() + "::" + group->getGroupName());
						} else {
							group->setGroupName(sEntity.getName());
						}

						//Check if group with equal name is already present.
						//If yes, we do not want a new group but instead
						//overwrite the existing one.
						for (const auto &g : _sensorGroups) {
							if ((g->getGroupName() == group->getGroupName()) && (&sEntity == g->getEntity())) {
								group = g;
								overwriting = true;
								break;
							}
						}

407
408
409
410
411
						//group which consists of only one sensor
						if (readSensorGroup(*group, val.second)) {
							group->setEntity(&sEntity);
							SB_Ptr sensor;
							//perhaps one sensor is already present because it was copied from the template group
412
413
414
							if (group->getDerivedSensors().size() != 0) {
								sensor = group->getDerivedSensors()[0];
								sensor->setName(val.second.data());
415
								if (readSensorBase(*sensor, val.second) && !overwriting) {
416
									storeSensorGroup(group);
417
418
								} else {
									LOG(warning) << "Single " << _baseName << " "
419
										     << val.second.data() << " could not be read! Omitting";
420
421
422
423
424
								}
							} else {
								sensor = std::make_shared<SBase>(val.second.data());
								if (readSensorBase(*sensor, val.second)) {
									group->pushBackSensor(sensor);
425
426
427
									if (!overwriting) {
										storeSensorGroup(group);
									}
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
								} 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...";
						}
					}
				}
			}
		}

		return true;
	}
	///@}

	///@name Overwrite in plugin
	///@{
	/**
     * Method responsible for reading plugin-specific sensor entity values.
     *
     * @param s         The sensor entity for which to set the values
     * @param config    A boost property (sub-)tree containing the entity values
     */
	virtual void sensorEntity(SEntity &s, CFG_VAL config) = 0;
	///@}

	///@name Utility
	///@{
	/**
     * @brief Adjusts the names of the sensors in generated groups.
     *
     * @return True if successful, false otherwise.
     */
	virtual bool constructSensorTopics() final override {
		// Sensor names are adjusted according to the respective MQTT topics
		for (auto &g : _sensorGroups) {
			for (auto &s : g->acquireSensors()) {
468
469
470
471
472
473
474
475
				std::string mqtt = MQTTChecker::formatTopic(_mqttPrefix);
				if (g->getEntity()) {
					mqtt.append(MQTTChecker::formatTopic(g->getEntity()->getMqttPart()));
				}
				mqtt.append(MQTTChecker::formatTopic(g->getMqttPart()));
				mqtt.append(MQTTChecker::formatTopic(s->getMqtt()));
				s->setMqtt(mqtt);
				s->setName(mqtt);
476
477
				SensorMetadata *sm = s->getMetadata();
				if (sm) {
478
479
480
481
482
					sm->setPublicName(s->getMqtt());
					sm->setPattern(s->getMqtt());
					sm->setIsVirtual(false);
					if (!sm->getInterval())
						sm->setInterval((uint64_t)g->getInterval() * 1000000);
483
484
					sm->setDelta(s->getDelta());

485
486
487
488
489
490
491
492
493
494
				}
			}
			g->releaseSensors();
		}
		return true;
	}
	///@}

	std::string _entityName;

495
	LOGGER lg;
496
497
498
499
500
	std::vector<SEntity *> _sensorEntitys;
	sEntityMap_t           _templateSensorEntitys;
};

#endif /* DCDBPUSHER_INCLUDES_CONFIGURATORTEMPLATEENTITY_H_ */