summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am2
-rw-r--r--src/callback.hpp79
-rw-r--r--src/count.hpp82
-rw-r--r--src/example/example.yaml27
-rwxr-xr-xsrc/pdmgen.py98
-rw-r--r--src/templates/conditional.mako.cpp3
-rw-r--r--src/templates/count.mako.cpp4
-rw-r--r--src/templates/generated.mako.hpp17
8 files changed, 312 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index c77c0a8..4d6658d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -23,6 +23,8 @@ CLEANFILES = generated.hpp
TEMPLATES = \
templates/callbackgroup.mako.cpp \
+ templates/conditional.mako.cpp \
+ templates/count.mako.cpp \
templates/generated.mako.hpp \
templates/journal.mako.cpp
diff --git a/src/callback.hpp b/src/callback.hpp
index 7c6f69b..f7886cb 100644
--- a/src/callback.hpp
+++ b/src/callback.hpp
@@ -28,6 +28,50 @@ class Callback
virtual void operator()() = 0;
};
+/** @class Conditional
+ * @brief Condition interface.
+ *
+ * Conditions of any type can be tested for true or false.
+ */
+class Conditional
+{
+ public:
+ Conditional() = default;
+ Conditional(const Conditional&) = delete;
+ Conditional(Conditional&&) = default;
+ Conditional& operator=(const Conditional&) = delete;
+ Conditional& operator=(Conditional&&) = default;
+ virtual ~Conditional() = default;
+
+ /** @brief Test the condition. */
+ virtual bool operator()() = 0;
+};
+
+/** @class IndexedConditional
+ * @brief Condition with an index.
+ */
+class IndexedConditional : public Conditional
+{
+ public:
+ IndexedConditional() = delete;
+ IndexedConditional(const IndexedConditional&) = delete;
+ IndexedConditional(IndexedConditional&&) = default;
+ IndexedConditional& operator=(const IndexedConditional&) = delete;
+ IndexedConditional& operator=(IndexedConditional&&) = default;
+ virtual ~IndexedConditional() = default;
+
+ explicit IndexedConditional(const PropertyIndex& conditionIndex)
+ : Conditional(), index(conditionIndex) {}
+
+ /** @brief Test the condition. */
+ virtual bool operator()() override = 0;
+
+ protected:
+
+ /** @brief Property names and their associated storage. */
+ const PropertyIndex& index;
+};
+
/** @class IndexedCallback
* @brief Callback with an index.
*/
@@ -90,6 +134,41 @@ class GroupOfCallbacks : public Callback
const std::vector<size_t>& graph;
};
+/** @class ConditionalCallback
+ * @brief Callback adaptor that asssociates a condition with a callback.
+ */
+template <typename CallbackAccess>
+class ConditionalCallback: public Callback
+{
+ public:
+ ConditionalCallback() = delete;
+ ConditionalCallback(const ConditionalCallback&) = delete;
+ ConditionalCallback(ConditionalCallback&&) = default;
+ ConditionalCallback& operator=(const ConditionalCallback&) = delete;
+ ConditionalCallback& operator=(ConditionalCallback&&) = default;
+ ~ConditionalCallback() = default;
+ ConditionalCallback(
+ const std::vector<size_t>& graphEntry,
+ Conditional& cond)
+ : graph(graphEntry), condition(cond) {}
+
+ /** @brief Run the callback if the condition is satisfied. */
+ void operator()() override
+ {
+ if (condition())
+ {
+ (*CallbackAccess::get()[graph[0]])();
+ }
+ }
+
+ private:
+ /** @brief The index of the callback to conditionally invoke. */
+ const std::vector<size_t>& graph;
+
+ /** @brief The condition to test. */
+ Conditional& condition;
+};
+
} // namespace monitoring
} // namespace dbus
} // namespace phosphor
diff --git a/src/count.hpp b/src/count.hpp
new file mode 100644
index 0000000..a588ccc
--- /dev/null
+++ b/src/count.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "callback.hpp"
+#include "data_types.hpp"
+
+namespace phosphor
+{
+namespace dbus
+{
+namespace monitoring
+{
+
+/** @class CountCondition
+ * @brief Count properties that satisfy a condition.
+ *
+ * When invoked, a count class instance performs its condition
+ * test in two passes.
+ *
+ * In pass one, apply a C++ relational operator to the value of
+ * each property in the index and a value provided by the
+ * configuration file.
+ *
+ * Count the number of properties that pass the test in pass
+ * one. In pass two, apply a second C++ relational operator
+ * to the number of properties that pass the test from pass one
+ * to a count provided by the configuration file.
+ */
+template <typename T>
+class CountCondition : public IndexedConditional
+{
+ public:
+ CountCondition() = delete;
+ CountCondition(const CountCondition&) = default;
+ CountCondition(CountCondition&&) = default;
+ CountCondition& operator=(const CountCondition&) = default;
+ CountCondition& operator=(CountCondition&&) = default;
+ ~CountCondition() = default;
+
+ CountCondition(
+ const PropertyIndex& conditionIndex,
+ const std::function<bool(size_t)>& _countOp,
+ const std::function<bool(T)>& _propertyOp) :
+ IndexedConditional(conditionIndex),
+ countOp(_countOp),
+ propertyOp(_propertyOp) {}
+
+ bool operator()() override
+ {
+ // Count the number of properties in the index that
+ // pass the condition specified in the config file.
+ auto count = std::count_if(
+ index.cbegin(),
+ index.cend(),
+ [this](const auto & item)
+ // *INDENT-OFF*
+ {
+ const auto& storage = std::get<2>(
+ item.second);
+ // Don't count properties that don't exist.
+ if (storage.get().empty())
+ {
+ return false;
+ }
+ const auto& value = any_ns::any_cast<T>(
+ storage);
+ return propertyOp(value);
+ });
+ // *INDENT-ON*
+
+ // Now apply the count condition to the count.
+ return countOp(count);
+ }
+
+ private:
+ /** @brief The comparison to perform on the count. */
+ std::function<bool(size_t)> countOp;
+ /** @brief The comparison to perform on each property. */
+ std::function<bool(T)> propertyOp;
+};
+} // namespace monitoring
+} // namespace dbus
+} // namespace phosphor
diff --git a/src/example/example.yaml b/src/example/example.yaml
index 0f286dc..65f6537 100644
--- a/src/example/example.yaml
+++ b/src/example/example.yaml
@@ -93,3 +93,30 @@
callback: group
members:
- example journal callback
+
+- name: example count condition
+ description: >
+ 'Conditions or conditional callbacks apply a test prior to invoking
+ the callback function.
+
+ All conditional callbacks must specify the callback to issue if
+ the condition evaulates.
+
+ The count condition applies the op comparison operator to the value of each
+ property in the specified groups. It then counts the number of properties
+ that pass the comparison, and applies another comparison on the result
+ against the specified bound.
+
+ For example, a callback that requires at least three temperature sensors
+ in the group to be higher than 115 degrees might use a count condition
+ with an op of >, a count op of >=, a bound of 115, and a countbound of 3.'
+
+ class: condition
+ condition: count
+ paths: example path group
+ properties: example property group
+ callback: example callback group
+ countop: '>='
+ countbound: 3
+ op: '>='
+ bound: 115
diff --git a/src/pdmgen.py b/src/pdmgen.py
index b20436e..4a958e1 100755
--- a/src/pdmgen.py
+++ b/src/pdmgen.py
@@ -420,6 +420,99 @@ class Callback(HasPropertyIndex):
super(Callback, self).__init__(**kw)
+class ConditionCallback(ConfigEntry, Renderer):
+ '''Handle the journal callback config file directive.'''
+
+ def __init__(self, *a, **kw):
+ self.condition = kw.pop('condition')
+ self.instance = kw.pop('instance')
+ super(ConditionCallback, self).__init__(**kw)
+
+ def factory(self, objs):
+ '''Create a graph instance for this callback.'''
+
+ args = {
+ 'configfile': self.configfile,
+ 'members': [self.instance],
+ 'class': 'callbackgroup',
+ 'callbackgroup': 'callback',
+ 'name': [self.instance]
+ }
+
+ entry = CallbackGraphEntry(**args)
+ add_unique(entry, objs, config=self.configfile)
+
+ super(ConditionCallback, self).factory(objs)
+
+ def setup(self, objs):
+ '''Resolve condition and graph entry.'''
+
+ self.graph = get_index(
+ objs,
+ 'callbackgroup',
+ [self.instance],
+ config=self.configfile)
+
+ self.condition = get_index(
+ objs,
+ 'condition',
+ self.name,
+ config=self.configfile)
+
+ super(ConditionCallback, self).setup(objs)
+
+ def construct(self, loader, indent):
+ return self.render(
+ loader,
+ 'conditional.mako.cpp',
+ c=self,
+ indent=indent)
+
+
+class Condition(HasPropertyIndex):
+ '''Interface and common logic for conditions.'''
+
+ def __init__(self, *a, **kw):
+ self.callback = kw.pop('callback')
+ super(Condition, self).__init__(**kw)
+
+ def factory(self, objs):
+ '''Create a callback instance for this conditional.'''
+
+ args = {
+ 'configfile': self.configfile,
+ 'condition': self.name,
+ 'class': 'callback',
+ 'callback': 'conditional',
+ 'instance': self.callback,
+ 'name': self.name,
+ }
+
+ callback = ConditionCallback(**args)
+ add_unique(callback, objs, config=self.configfile)
+ callback.factory(objs)
+
+ super(Condition, self).factory(objs)
+
+
+class CountCondition(Condition, Renderer):
+ '''Handle the count condition config file directive.'''
+
+ def __init__(self, *a, **kw):
+ self.countop = kw.pop('countop')
+ self.countbound = kw.pop('countbound')
+ self.op = kw.pop('op')
+ self.bound = kw.pop('bound')
+ super(CountCondition, self).__init__(**kw)
+
+ def construct(self, loader, indent):
+ return self.render(
+ loader,
+ 'count.mako.cpp',
+ c=self,
+ indent=indent)
+
+
class Journal(Callback, Renderer):
'''Handle the journal callback config file directive.'''
@@ -526,6 +619,9 @@ class Everything(Renderer):
'journal': Journal,
'group': GroupOfCallbacks,
},
+ 'condition': {
+ 'count': CountCondition,
+ },
}
if cls not in class_map:
@@ -617,6 +713,7 @@ class Everything(Renderer):
self.watches = kw.pop('watch', [])
self.callbacks = kw.pop('callback', [])
self.callbackgroups = kw.pop('callbackgroup', [])
+ self.conditions = kw.pop('condition', [])
super(Everything, self).__init__(**kw)
@@ -640,6 +737,7 @@ class Everything(Renderer):
instancegroups=self.instancegroups,
callbacks=self.callbacks,
callbackgroups=self.callbackgroups,
+ conditions=self.conditions,
indent=Indent()))
if __name__ == '__main__':
diff --git a/src/templates/conditional.mako.cpp b/src/templates/conditional.mako.cpp
new file mode 100644
index 0000000..3cc9564
--- /dev/null
+++ b/src/templates/conditional.mako.cpp
@@ -0,0 +1,3 @@
+std::make_unique<ConditionalCallback<ConfigPropertyCallbacks>>(
+${indent(1)}ConfigPropertyCallbackGroups::get()[${c.graph}],
+${indent(1)}*ConfigConditions::get()[${c.condition}])\
diff --git a/src/templates/count.mako.cpp b/src/templates/count.mako.cpp
new file mode 100644
index 0000000..d348518
--- /dev/null
+++ b/src/templates/count.mako.cpp
@@ -0,0 +1,4 @@
+std::make_unique<CountCondition<${c.datatype}>>(
+${indent(1)}ConfigPropertyIndicies::get()[${c.instances}],
+${indent(1)}[](const auto& item){return item ${c.countop} ${c.countbound};},
+${indent(1)}[](const auto& item){return item ${c.op} ${c.bound};})\
diff --git a/src/templates/generated.mako.hpp b/src/templates/generated.mako.hpp
index 67b9149..a4819e2 100644
--- a/src/templates/generated.mako.hpp
+++ b/src/templates/generated.mako.hpp
@@ -4,6 +4,7 @@
#include <array>
#include <string>
+#include "count.hpp"
#include "data_types.hpp"
#include "journal.hpp"
#include "propertywatchimpl.hpp"
@@ -145,6 +146,22 @@ struct ConfigPropertyCallbackGroups
}
};
+struct ConfigConditions
+{
+ using Conditions = std::array<std::unique_ptr<Conditional>, ${len(conditions)}>;
+
+ static auto& get()
+ {
+ static const Conditions propertyConditions =
+ {
+% for c in conditions:
+ ${c.construct(loader, indent=indent +3)},
+% endfor
+ };
+ return propertyConditions;
+ }
+};
+
struct ConfigPropertyCallbacks
{
using Callbacks = std::array<std::unique_ptr<Callback>, ${len(callbacks)}>;
OpenPOWER on IntegriCloud