From c1283ae8472dbd4e2c3593b76b7600ac72640b80 Mon Sep 17 00:00:00 2001 From: Brad Bishop Date: Sat, 20 May 2017 21:42:38 -0400 Subject: Add support for callbacks Callbacks are the response in the PDM 'trigger->response' model. Add general support for implementing callbacks and implement a log to systemd journal using that framework. Signed-off-by: Brad Bishop Change-Id: I8bead5368ee5472a02b47e8bba9e9df3a1f346bc --- src/Makefile.am | 4 +- src/callback.hpp | 57 +++++++++++++++++++++++++ src/example/example.yaml | 21 ++++++++++ src/journal.cpp | 46 ++++++++++++++++++++ src/journal.hpp | 90 ++++++++++++++++++++++++++++++++++++++++ src/pdmgen.py | 28 +++++++++++++ src/propertywatchimpl.hpp | 6 +-- src/templates/generated.mako.hpp | 29 ++++++++++--- src/templates/journal.mako.cpp | 3 ++ 9 files changed, 273 insertions(+), 11 deletions(-) create mode 100644 src/callback.hpp create mode 100644 src/journal.cpp create mode 100644 src/journal.hpp create mode 100644 src/templates/journal.mako.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 3b4d9cf..f223713 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,6 +7,7 @@ sbin_PROGRAMS = phosphor-dbus-monitor phosphor_dbus_monitor_SOURCES = \ functor.cpp \ + journal.cpp \ main.cpp \ monitor.cpp \ propertywatch.cpp @@ -21,7 +22,8 @@ BUILT_SOURCES = generated.hpp CLEANFILES = generated.hpp TEMPLATES = \ - templates/generated.mako.hpp + templates/generated.mako.hpp \ + templates/journal.mako.cpp generated.hpp: $(PDMGEN) $(YAML_PATH) $(TEMPLATES) $(AM_V_GEN)$(PYTHON) ${PDMGEN} \ diff --git a/src/callback.hpp b/src/callback.hpp new file mode 100644 index 0000000..d7a678c --- /dev/null +++ b/src/callback.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "data_types.hpp" + +namespace phosphor +{ +namespace dbus +{ +namespace monitoring +{ + +/** @class Callback + * @brief Callback interface. + * + * Callbacks of any type can be run. + */ +class Callback +{ + public: + Callback() = default; + Callback(const Callback&) = delete; + Callback(Callback&&) = default; + Callback& operator=(const Callback&) = delete; + Callback& operator=(Callback&&) = default; + virtual ~Callback() = default; + + /** @brief Run the callback. */ + virtual void operator()() = 0; +}; + +/** @class IndexedCallback + * @brief Callback with an index. + */ +class IndexedCallback : public Callback +{ + public: + IndexedCallback() = delete; + IndexedCallback(const IndexedCallback&) = delete; + IndexedCallback(IndexedCallback&&) = default; + IndexedCallback& operator=(const IndexedCallback&) = delete; + IndexedCallback& operator=(IndexedCallback&&) = default; + virtual ~IndexedCallback() = default; + explicit IndexedCallback(const PropertyIndex& callbackIndex) + : Callback(), index(callbackIndex) {} + + /** @brief Run the callback. */ + virtual void operator()() override = 0; + + protected: + + /** @brief Property names and their associated storage. */ + const PropertyIndex& index; +}; + +} // namespace monitoring +} // namespace dbus +} // namespace phosphor diff --git a/src/example/example.yaml b/src/example/example.yaml index 4e216cd..171aeaa 100644 --- a/src/example/example.yaml +++ b/src/example/example.yaml @@ -53,3 +53,24 @@ watch: property paths: example path group properties: example property group + +- name: example journal callback + description: > + 'Callbacks are actions PDM should take when instructed to do so. + + Some callback types refer to a group of paths and group of properties + in a similar fashion as the property watch directive. + + The journal callback logs the specified message to the systemd journal + with the specified severity. + + Additionally, the journal callback will add to the journal key value + pair metadata for each property in the specified property group with + the key being the property element metadata and the value being the + property value.' + class: callback + callback: journal + paths: example path group + properties: example property group + severity: INFO + message: Hello world from PDM! diff --git a/src/journal.cpp b/src/journal.cpp new file mode 100644 index 0000000..46f74e9 --- /dev/null +++ b/src/journal.cpp @@ -0,0 +1,46 @@ +/** + * Copyright © 2017 IBM Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "journal.hpp" + +namespace phosphor +{ +namespace dbus +{ +namespace monitoring +{ + +void JournalBase::operator()() +{ + for (const auto& n : index) + { + const auto& path = std::get<0>(n.first); + const auto& pathMeta = std::get<0>(n.second); + const auto& propertyMeta = std::get<1>(n.second); + const auto& value = std::get<2>(n.second); + + if (!value.get().empty()) + { + log(message, + pathMeta, + path, + propertyMeta, + value); + } + } +} +} // namespace monitoring +} // namespace dbus +} // namespace phosphor diff --git a/src/journal.hpp b/src/journal.hpp new file mode 100644 index 0000000..4e47670 --- /dev/null +++ b/src/journal.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include "callback.hpp" +#include "format.hpp" + +namespace phosphor +{ +namespace dbus +{ +namespace monitoring +{ + +/** @class JournalBase + * @brief Journal callback implementation. + * + * The journal callback logs the client message and + * journal metadata key value pairs as specified by the + * client supplied property index. + */ +class JournalBase : public IndexedCallback +{ + public: + JournalBase() = delete; + JournalBase(const JournalBase&) = delete; + JournalBase(JournalBase&&) = default; + JournalBase& operator=(const JournalBase&) = delete; + JournalBase& operator=(JournalBase&&) = default; + virtual ~JournalBase() = default; + JournalBase(const char* msg, const PropertyIndex& index) : + IndexedCallback(index), message(msg) {} + + /** @brief Callback interface implementation. */ + void operator()() override; + + private: + /** @brief Delegate type specific calls to subclasses. */ + virtual void log( + const char* message, + const std::string& pathMeta, + const std::string& path, + const std::string& propertyMeta, + const any_ns::any& value) const = 0; + + /** @brief The client provided message to be traced. */ + const char* message; +}; + +/** @class Journal + * @brief C++ type specific logic for the journal callback. + * + * @tparam T - The C++ type of the property values being traced. + * @tparam Severity - The log severity of the log entry. + */ +template +class Journal : public JournalBase +{ + public: + Journal() = delete; + Journal(const Journal&) = delete; + Journal(Journal&&) = default; + Journal& operator=(const Journal&) = delete; + Journal& operator=(Journal&&) = default; + ~Journal() = default; + Journal(const char* msg, const PropertyIndex& index) : + JournalBase(msg, index) {} + + private: + /** @brief log interface implementation. */ + void log( + const char* message, + const std::string& pathMeta, + const std::string& path, + const std::string& propertyMeta, + const any_ns::any& value) const override + { + phosphor::logging::log( + message, + phosphor::logging::entry( + pathMeta + GetFormat::format, + path), + phosphor::logging::entry( + propertyMeta + GetFormat::format, + any_ns::any_cast(value))); + } +}; + +} // namespace monitoring +} // namespace dbus +} // namespace phosphor diff --git a/src/pdmgen.py b/src/pdmgen.py index 070c118..e4a97e7 100755 --- a/src/pdmgen.py +++ b/src/pdmgen.py @@ -413,6 +413,29 @@ class PropertyWatch(HasPropertyIndex): super(PropertyWatch, self).__init__(**kw) +class Callback(HasPropertyIndex): + '''Interface and common logic for callbacks.''' + + def __init__(self, *a, **kw): + super(Callback, self).__init__(**kw) + + +class Journal(Callback, Renderer): + '''Handle the journal callback config file directive.''' + + def __init__(self, *a, **kw): + self.severity = kw.pop('severity') + self.message = kw.pop('message') + super(Journal, self).__init__(**kw) + + def construct(self, loader, indent): + return self.render( + loader, + 'journal.mako.cpp', + c=self, + indent=indent) + + class Everything(Renderer): '''Parse/render entry point.''' @@ -440,6 +463,9 @@ class Everything(Renderer): 'instance': { 'element': Instance, }, + 'callback': { + 'journal': Journal, + }, } if cls not in class_map: @@ -529,6 +555,7 @@ class Everything(Renderer): self.instances = kw.pop('instance', []) self.instancegroups = kw.pop('instancegroup', []) self.watches = kw.pop('watch', []) + self.callbacks = kw.pop('callback', []) super(Everything, self).__init__(**kw) @@ -550,6 +577,7 @@ class Everything(Renderer): instances=self.instances, watches=self.watches, instancegroups=self.instancegroups, + callbacks=self.callbacks, indent=Indent())) if __name__ == '__main__': diff --git a/src/propertywatchimpl.hpp b/src/propertywatchimpl.hpp index 46799ed..6e957ae 100644 --- a/src/propertywatchimpl.hpp +++ b/src/propertywatchimpl.hpp @@ -32,10 +32,6 @@ void PropertyWatch::start() { return; } - else - { - alreadyRan = true; - } // The index has a flat layout which is not optimal here. Nest // properties in a map of interface names in a map of object paths. @@ -107,6 +103,8 @@ void PropertyWatch::start() } } } + + alreadyRan = true; } template diff --git a/src/templates/generated.mako.hpp b/src/templates/generated.mako.hpp index 0911e3a..61c2b6f 100644 --- a/src/templates/generated.mako.hpp +++ b/src/templates/generated.mako.hpp @@ -5,6 +5,7 @@ #include #include #include "data_types.hpp" +#include "journal.hpp" #include "propertywatchimpl.hpp" #include "sdbusplus.hpp" @@ -107,15 +108,15 @@ struct ConfigPropertyIndicies { PropertyIndex::key_type { - ConfigPaths::get()[${i[0]}], - ConfigInterfaces::get()[${i[2]}], - ConfigProperties::get()[${i[3]}] + std::cref(ConfigPaths::get()[${i[0]}]), + std::cref(ConfigInterfaces::get()[${i[2]}]), + std::cref(ConfigProperties::get()[${i[3]}]) }, PropertyIndex::mapped_type { - ConfigMeta::get()[${i[1]}], - ConfigMeta::get()[${i[4]}], - ConfigPropertyStorage::get()[${i[5]}] + std::cref(ConfigMeta::get()[${i[1]}]), + std::cref(ConfigMeta::get()[${i[4]}]), + std::ref(ConfigPropertyStorage::get()[${i[5]}]) }, }, % endfor @@ -127,6 +128,22 @@ struct ConfigPropertyIndicies } }; +struct ConfigPropertyCallbacks +{ + using Callbacks = std::array, ${len(callbacks)}>; + + static auto& get() + { + static const Callbacks propertyCallbacks = + { +% for c in callbacks: + ${c.construct(loader, indent=indent +3)}, +% endfor + }; + return propertyCallbacks; + } +}; + struct ConfigPropertyWatches { using PropertyWatches = std::array, ${len(watches)}>; diff --git a/src/templates/journal.mako.cpp b/src/templates/journal.mako.cpp new file mode 100644 index 0000000..787337d --- /dev/null +++ b/src/templates/journal.mako.cpp @@ -0,0 +1,3 @@ +std::make_unique>( +${indent(1)}"${c.message}", +${indent(1)}ConfigPropertyIndicies::get()[${c.instances}])\ -- cgit v1.2.1