//================================================================================ // Name : metadatastore.h // Author : Alessio Netti // Contact : info@dcdb.it // Copyright : Leibniz Supercomputing Centre // Description : A meta-data store for sensors //================================================================================ //================================================================================ // This file is part of DCDB (DataCenter DataBase) // Copyright (C) 2011-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 PROJECT_METADATASTORE_H #define PROJECT_METADATASTORE_H #include #include #include #include #include #include #include #include #include #include #include "globalconfiguration.h" using namespace std; class SensorMetadata { public: SensorMetadata() : isOperation(false), isVirtual(false), integrable(true), monotonic(false), publicName(""), pattern(""), unit(""), scale(1.0), ttl(0), interval(0), operations("") {} SensorMetadata(const SensorMetadata& other) { this->isOperation = other.isOperation; this->isVirtual = other.isVirtual; this->integrable = other.integrable; this->monotonic = other.monotonic; this->publicName = other.publicName; this->pattern = other.pattern; this->unit = other.unit; this->scale = other.scale; this->ttl = other.ttl; this->interval = other.interval; this->operations = other.operations; } SensorMetadata& operator=(const SensorMetadata& other) { this->isOperation = other.isOperation; this->isVirtual = other.isVirtual; this->integrable = other.integrable; this->monotonic = other.monotonic; this->publicName = other.publicName; this->pattern = other.pattern; this->unit = other.unit; this->scale = other.scale; this->ttl = other.ttl; this->interval = other.interval; this->operations = other.operations; return *this; } bool addOperation(const string& opName) { if (publicName.length()>0 && opName.length()>publicName.length() && !opName.compare(0, publicName.length(), publicName)) { if(operations.length()==0) operations = opName.substr(publicName.length()); else operations += "," + opName.substr(publicName.length()); return true; } else return false; } /** * @brief Parses a JSON string and stores the content in this object. * * If parsing fails, a InvalidArgument exception is thrown. * * @param payload JSON-encoded string containing metadata information */ void parseJSON(const string& payload) { boost::property_tree::iptree config; std::istringstream str(payload); boost::property_tree::read_json(str, config); parsePTREE(config); } /** * @brief Parses a PTREE INFO block and stores the content in this object. * * If parsing fails, a InvalidArgument exception is thrown. * * @param config PTREE block containing metadata */ void parsePTREE(boost::property_tree::iptree& config) { BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) { if (boost::iequals(val.first, "monotonic")) { this->monotonic = to_bool(val.second.data()); } else if (boost::iequals(val.first, "isVirtual")) { this->isVirtual = to_bool(val.second.data()); } else if (boost::iequals(val.first, "isOperation")) { this->isOperation = to_bool(val.second.data()); } else if (boost::iequals(val.first, "integrable")) { this->integrable = to_bool(val.second.data()); } else if (boost::iequals(val.first, "unit")) { this->unit = val.second.data(); } else if (boost::iequals(val.first, "publicName")) { this->publicName = val.second.data(); } else if (boost::iequals(val.first, "pattern")) { this->pattern = val.second.data(); } else if (boost::iequals(val.first, "scale")) { this->scale = stod(val.second.data()); } else if (boost::iequals(val.first, "interval")) { this->interval = stoull(val.second.data()) * 1000000; } else if (boost::iequals(val.first, "ttl")) { this->ttl = stoull(val.second.data()) * 1000000; } else if (boost::iequals(val.first, "operations")) { this->operations = _sanitizeOperations(val.second.data()); } } } /** * @brief Converts the content of this object into JSON format. * * @return String containing the JSON representation of this object */ string getJSON() const { boost::property_tree::ptree config; std::ostringstream output; _dumpPTREE(config); boost::property_tree::write_json(output, config, true); return output.str(); } /** * @brief Returns a sensorMetadata_t object from the internal map, converted into PTREE format. * * @return A PTREE object representing this SensorMetadata object */ boost::property_tree::ptree getPTREE() const { boost::property_tree::ptree config; _dumpPTREE(config); return config; } // Public class members bool isOperation; bool isVirtual; bool integrable; bool monotonic; string publicName; string pattern; string unit; double scale; uint64_t ttl; uint64_t interval; string operations; protected: // Parses a operations string and sanitizes it from excess whitespace string _sanitizeOperations(const string& str, const char sep=',') { vector v; string out=""; // We split the string into the comma-separated tokens std::stringstream ss(str); std::string token; while (std::getline(ss, token, sep)) { if(!token.empty()) { boost::algorithm::trim(token); v.push_back(token); } } // We re-write the vector into a string, this time properly formatted string sepStr = string(1,sep); for(const auto& el : v) out += el + sepStr; if(!out.empty() && out.back() == sep) out.erase(out.size()-1, 1); return out; } // Dumps the contents of "s" in "config" void _dumpPTREE(boost::property_tree::ptree& config) const { std::ostringstream scaleStream; scaleStream << this->scale; config.clear(); config.push_back(boost::property_tree::ptree::value_type("isOperation", boost::property_tree::ptree(this->isOperation ? "true" : "false"))); config.push_back(boost::property_tree::ptree::value_type("isVirtual", boost::property_tree::ptree(this->isVirtual ? "true" : "false"))); config.push_back(boost::property_tree::ptree::value_type("monotonic", boost::property_tree::ptree(this->monotonic ? "true" : "false"))); config.push_back(boost::property_tree::ptree::value_type("integrable", boost::property_tree::ptree(this->integrable ? "true" : "false"))); config.push_back(boost::property_tree::ptree::value_type("unit", boost::property_tree::ptree(this->unit))); config.push_back(boost::property_tree::ptree::value_type("publicName", boost::property_tree::ptree(this->publicName))); config.push_back(boost::property_tree::ptree::value_type("pattern", boost::property_tree::ptree(this->pattern))); config.push_back(boost::property_tree::ptree::value_type("scale", boost::property_tree::ptree(scaleStream.str()))); config.push_back(boost::property_tree::ptree::value_type("interval", boost::property_tree::ptree(to_string(this->interval / 1000000)))); config.push_back(boost::property_tree::ptree::value_type("ttl", boost::property_tree::ptree(to_string(this->ttl / 1000000)))); config.push_back(boost::property_tree::ptree::value_type("operations", boost::property_tree::ptree(this->operations))); } }; // --------------------------------------------------------------------------- // --------------------------- METADATASTORE CLASS --------------------------- // --------------------------------------------------------------------------- class MetadataStore { public: /** * @brief Public class constructor. */ MetadataStore() {} /** * @brief Class destructor. */ ~MetadataStore() {} /** * @brief Clears the internal metadata map. */ void clear() { _metadata.clear(); } /** * @brief Returns the internal metadata map. * * @return A string to Metadata_t map */ const unordered_map& getMap() { return _metadata; } /** * @brief Stores a sensorMetadata_t object in the internal map. * * If the input key already exists in the map, the entry is overwritten. * * @param key Sensor key under which metadata must be stored * @param s Object containing sensor metadata * @return True if "key" is unique, False if there was a collision */ bool store(const string& key, const SensorMetadata& s) { bool overwritten = !_metadata.count(key); _metadata[key] = s; return overwritten; } /** * @brief Stores a sensorMetadata_t object in the internal map, after parsing it from a JSON string. * * If the input key already exists in the map, the entry is overwritten. If parsing fails, * a InvalidArgument exception is thrown. * * @param key Sensor key under which metadata must be stored * @param payload JSON-encoded string containing metadata information * @return True if "key" is unique, False if there was a collision */ bool storeFromJSON(const string& key, const string& payload) { SensorMetadata metadata; metadata.parseJSON(payload); return store(key, metadata); } /** * @brief Stores a sensorMetadata_t object in the internal map, after parsing it from a INFO block. * * If the input key already exists in the map, the entry is overwritten. If parsing fails, * a InvalidArgument exception is thrown. * * @param key Sensor key under which metadata must be stored * @param config PTREE block containing metadata * @return True if "key" is unique, False if there was a collision */ bool storeFromPTREE(const string& key, boost::property_tree::iptree& config) { SensorMetadata metadata; metadata.parsePTREE(config); return store(key, metadata); } /** * @brief Returns a sensorMetadata_t object from the internal map. * * If the input key does not exist in the map, a InvalidArgument exception is thrown. * * @param key Sensor key to be queried * @return A reference to a sensorMetadata_t object */ const SensorMetadata& get(const string& key) { if(!_metadata.count(key)) throw invalid_argument("MetadataStore: key " + key + " does not exist!"); return _metadata[key]; } /** * @brief Returns the TTL of a sensorMetadata_t object from the internal map. * * If the input key does not exist in the map, the value -1 is returned. This method exists * to boost (slightly) look-up performance in the CollectAgent, which requires TTL values * when performing database inserts. * * @param key Sensor key to be queried * @return TTL value in seconds */ int64_t getTTL(const string& key) { auto it = _metadata.find(key); return it==_metadata.end() ? -1 : it->second.ttl/1000000000; } /** * @brief Returns a sensorMetadata_t object from the internal map, converted into JSON format. * * If the input key does not exist in the map, a InvalidArgument exception is thrown. * * @param key Sensor key to be queried * @return String containing the JSON representation of the sensorMetadata_t object */ string getJSON(const string& key) { return this->get(key).getJSON(); } /** * @brief Returns a sensorMetadata_t object from the internal map, converted into PTREE format. * * If the input key does not exist in the map, a InvalidArgument exception is thrown. * * @param key Sensor key to be queried * @return A PTREE object representing the sensorMetadata_t object */ boost::property_tree::ptree getPTREE(const string& key) { return this->get(key).getPTREE(); } protected: unordered_map _metadata; }; #endif //PROJECT_METADATASTORE_H