mqttchecker.h 9.9 KB
Newer Older
1
2
3
//================================================================================
// Name        : mqttchecker.h
// Author      : Alessio Netti
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
// Copyright   : Leibniz Supercomputing Centre
// Description : Class that manages constraints for MQTT topic formatting.
//================================================================================

//================================================================================
// 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.
Alessio Netti's avatar
Alessio Netti committed
17
//
18
19
20
21
// 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.
Alessio Netti's avatar
Alessio Netti committed
22
//
23
24
25
26
// 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.
//================================================================================
Alessio Netti's avatar
Alessio Netti committed
27
28
29
30
31

#ifndef PROJECT_MQTTCHECKER_H
#define PROJECT_MQTTCHECKER_H

#include <set>
Alessio Netti's avatar
Alessio Netti committed
32
#include <algorithm>
Alessio Netti's avatar
Alessio Netti committed
33
34
#include "logging.h"

35
#define MQTT_SEP '/'
36
#define NAME_SEP '.'
37
#define JOB_STR "job"
38

Alessio Netti's avatar
Alessio Netti committed
39
/**
40
 * @brief Class that manages constraint for MQTT topic formatting
Alessio Netti's avatar
Alessio Netti committed
41
 *
42
43
44
45
46
 * @details This class is implemented as a singleton. It maintains an internal
 *          set containing the currently used MQTT topics. Each used sensor
 *          topic should be checked with the "check" method.
 *
 * @ingroup common
Alessio Netti's avatar
Alessio Netti committed
47
48
49
50
 */
class MQTTChecker {

public:
51
    
Alessio Netti's avatar
Alessio Netti committed
52
53
54
55
56
57
58
59
60
61
62
63
64
    /**
    * @brief            Returns an instance to a MQTTChecker object
    *
    *                   The MQTTChecker class is implemented as a singleton: therefore, all entities calling the
    *                   getInstance method will share the same instance of the MQTTChecker class.
    *
    * @returns          Reference to a MQTTChecker object
    */
    static MQTTChecker& getInstance() {
        static MQTTChecker m;
        return m;
    }

65
66
67
    /**
     * @brief           Converts a numerical job ID to its internal MQTT topic representation
     * 
Micha Müller's avatar
Micha Müller committed
68
     * @param jobId     The job ID value to be processed
69
70
     * @return          The processed MQTT topic
     */
71
72
    static std::string jobToTopic(std::string jobId) {
        return "/job" + jobId + "/";
73
74
75
76
77
78
79
80
    }

    /**
     * @brief           Converts an MQTT topic to its corresponding numerical job ID, if applicable
     * 
     * @param topic     The topic to be processed 
     * @return          The numerical job ID
     */
81
    static std::string topicToJob(const std::string& topic) {
82
83
84
85
86
        std::string jobKey(JOB_STR), jobId = topic;
        jobId.erase(std::remove(jobId.begin(), jobId.end(), MQTT_SEP), jobId.end());
        size_t pos = jobId.find(jobKey);
        if (pos != std::string::npos) 
            jobId.erase(pos, jobKey.length());
87
        return jobId;
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    }
    
    /**
     * @brief           Converts an MQTT topic to a filesystem path representation
     * 
     * @param topic     The MQTT topic to be processed 
     * @param path      The base destination path
     * @return          A processed file system path
     */
    static std::string topicToFile(const std::string& topic, const std::string& path="") {
        std::string tPath = path;
        if(tPath.empty())
            tPath = "./";
        else if(tPath[tPath.size()-1]!='/')
            tPath += '/';
        return tPath + topicToName(topic);
104
105
    }

Alessio Netti's avatar
Alessio Netti committed
106
    /**
107
     * @brief           Converts a sensor name to its internal MQTT topic representation
Alessio Netti's avatar
Alessio Netti committed
108
     * 
109
     * @param name      The sensor name to be processed 
Alessio Netti's avatar
Alessio Netti committed
110
111
     * @return          The processed MQTT topic
     */
112
113
114
115
116
    static std::string nameToTopic(const std::string& name) {
        if(name.empty()) return name;
        std::string newTopic = name;
        std::replace(newTopic.begin(), newTopic.end(), NAME_SEP, MQTT_SEP);
        if(!newTopic.empty() && newTopic[0] != MQTT_SEP) newTopic.insert(0, 1, MQTT_SEP);
Alessio Netti's avatar
Alessio Netti committed
117
118
        return newTopic;
    }
119
120
121
122
123
124
125
126
127
128
129
130
131
132

    /**
     * @brief           Converts an MQTT topic to the name representation exposed to users
     * 
     * @param topic     The topic to be processed 
     * @return          The processed sensor name
     */
    static std::string topicToName(const std::string& topic) {
        if(topic.empty()) return topic;
        std::string newName = topic;
        std::replace(newName.begin(), newName.end(), MQTT_SEP, NAME_SEP);
        if(!newName.empty() && newName[0] == NAME_SEP) newName.erase(0, 1);
        return newName;
    }
Alessio Netti's avatar
Alessio Netti committed
133
    
134
135
136
137
138
139
140
141
    /**
     * @brief           Sanitizes and formats a MQTT topic or suffix
     * 
     * @param topic     The topic or suffix to be processed 
     * @param cpuID     The cpu ID associated to this topic (if any)
     * @return          The processed MQTT topic or suffix
     */
    static std::string formatTopic(const std::string& topic, int cpuID=-1) {
Alessio Netti's avatar
Alessio Netti committed
142
143
144
145
146
147
148
149
        if(topic.empty()) return topic;
        std::string newTopic = topic;
        // Stripping off all forward slashes
        while(!newTopic.empty() && newTopic.front()==MQTT_SEP) newTopic.erase(0, 1);
        while(!newTopic.empty() && newTopic.back()==MQTT_SEP)  newTopic.erase(newTopic.size()-1, 1);
        // Adding the one front forward slash that we need
        newTopic.insert(0, 1, MQTT_SEP);
        return cpuID<0 ? newTopic : "/cpu" + std::to_string(cpuID) + newTopic;
150
151
    }

Alessio Netti's avatar
Alessio Netti committed
152
153
154
    /**
    * @brief            Resets the internal topics set
    */
Alessio Netti's avatar
Alessio Netti committed
155
156
157
158
159
160
161
162
163
164
165
166
    void reset()           { _topics.clear(); _groups.clear(); }

    /**
    * @brief            Removes a topic from the internal set
    *
    *                   This method should be used to remove obsolete topics from the set of active topics. This
    *                   usually happens when plugins are reloaded in dcdbpusher, and all old sensors are destroyed.
    *
    * @param topic      The topic to be removed
    */
    void removeTopic(const std::string& topic) {
        std::string str(topic);
167
        str.erase(std::remove(str.begin(), str.end(), MQTT_SEP), str.end());
Alessio Netti's avatar
Alessio Netti committed
168
169
        _topics.erase(str);
    }
Alessio Netti's avatar
Alessio Netti committed
170
171
172
173
174
175
176
177
178
179

    /**
    * @brief            Performs a check on a certain MQTT topic
    *
    *                   The check is passed if the topic is not present already in the internal set (to prevent duplicate
    *                   topics) and if all other internal checks are cleared.
    *
    * @param topic      An arbitrary MQTT topic to check
    * @return           True if the topic is valid, False otherwise
    */
Alessio Netti's avatar
Alessio Netti committed
180
    bool checkTopic(const std::string& topic) {
181
        //We remove all '/' characters to detect duplicates
Alessio Netti's avatar
Alessio Netti committed
182
        std::string str(topic);
183
        str.erase(std::remove(str.begin(), str.end(), MQTT_SEP), str.end());
Alessio Netti's avatar
Alessio Netti committed
184
185
186
187
188
189
190
191
        auto returnIt = _topics.insert(str);
        if (!returnIt.second) {
            LOG(error) << "MQTT-Topic \"" << topic << "\" used twice!";
            return false;
        }
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
    /**
    * @brief            Removes a name from the internal set of sensor names
    *
    *                   This method should be used to remove obsolete sensor names, e.g. when reloading plugins.
    *                   This is useful only in the case that MQTT topics differ from the actual sensor names.
    *
    * @param name      The name (string) to be removed
    */
    void removeName(const std::string& name) {
        _names.erase(name);
    }

    /**
    * @brief            Performs a check on a certain name
    *
    *                   The check is passed if the name for the group, analyzer or entity is not used already.
    *
    * @param name      An arbitrary name (string) to check
    * @return           True if the name is valid, False otherwise
    */
    bool checkName(const std::string& name) {
213
214
215
216
217
        auto returnIt = _names.insert(name);
        if (!returnIt.second) {
            LOG(error) << "Name \"" << name << "\" used twice!";
            return false;
        }
Alessio Netti's avatar
Alessio Netti committed
218
219
220
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
221
222
223
224
225
226
    /**
    * @brief            Removes a name from the internal set of entities
    *
    *                   This method should be used to remove obsolete sensor groups, entities or analyzers once they
    *                   are destroyed, e.g. on plugin reload actions.
    *
Alessio Netti's avatar
Alessio Netti committed
227
    * @param name      The name (string) to be removed
Alessio Netti's avatar
Alessio Netti committed
228
    */
Alessio Netti's avatar
Alessio Netti committed
229
    void removeGroup(const std::string& name) {
Alessio Netti's avatar
Alessio Netti committed
230
231
232
233
        _groups.erase(name);
    }

    /**
Alessio Netti's avatar
Alessio Netti committed
234
    * @brief            Performs a check on a certain group name
Alessio Netti's avatar
Alessio Netti committed
235
236
237
    *
    *                   The check is passed if the name for the group, analyzer or entity is not used already.
    *
Alessio Netti's avatar
Alessio Netti committed
238
    * @param name      An arbitrary name (string) to check
Alessio Netti's avatar
Alessio Netti committed
239
240
    * @return           True if the name is valid, False otherwise
    */
Alessio Netti's avatar
Alessio Netti committed
241
    bool checkGroup(const std::string& name) {
242
243
244
245
246
        auto returnIt = _groups.insert(name);
        if (!returnIt.second) {
            LOG(error) << "Group name \"" << name << "\" used twice!";
            return false;
        }
Alessio Netti's avatar
Alessio Netti committed
247
248
249
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
private:

    /**
    * @brief            Private class constructor
    */
    MQTTChecker() {}

    /**
    * @brief            Copy constructor is not available
    */
    MQTTChecker(MQTTChecker const&)     = delete;

    /**
    * @brief            Assignment operator is not available
    */
    void operator=(MQTTChecker const&)  = delete;

    // Set used to keep track of topics
    std::set<std::string> _topics;
Alessio Netti's avatar
Alessio Netti committed
269
270
    // Set used to keep track of groups, analyzers and other entities
    std::set<std::string> _groups;
Alessio Netti's avatar
Alessio Netti committed
271
272
    // Set used to keep track of sensor names (if different from the respective topics)
    std::set<std::string> _names;
Alessio Netti's avatar
Alessio Netti committed
273

Alessio Netti's avatar
Alessio Netti committed
274
275
276
277
278
    // Logger object to notify MQTT check outcome
    logger_t lg;
};

#endif //PROJECT_MQTTCHECKER_H