control: action to override a single fan in a zone - Action class
This change contains the OverrideFanTarget class to create the Action
from a json config. A very basic events.json would look like this:
[
{
"name": "test",
"groups": [
{ "name": "fan0 sensor inventory", "interface": "xyz.openbmc_project.State.Decorator.OperationalStatus",
"property": { "name": "Functional" } }
],
"triggers": [
{ "class": "init", "method": "get_properties" },
{ "class": "signal", "signal": "properties_changed" }
],
"actions": [ {
"name": "override_fan_target",
"count": 1,
"state": false,
"fans": [ "fan0", "fan2" ],
"target": 9999
} ]
}
]
When the tach sensor for fan0 is set to non-funtional:
root@p10bmc:~# busctl set-property xyz.openbmc_project.Inventory.Manager
/xyz/openbmc_project/inventory/system/chassis/motherboard/fan0/fan0_0
xyz.openbmc_project.State.Decorator.OperationalStatus Functional b false
the run() method for the Action is called and all registered fans will
be locked at the "target" value given in the json "actions" section.
...
<output trimmed>
...
FAN TARGET(RPM) FEEDBACKS(RPMS) PRESENT FUNCTIONAL
===============================================================
fan0 9999 7019/9999 true true
fan1 10000 7020/10000 true true
fan2 9999 7019/9999 true true
...
When the inventory registers the sensor back to functional, the override
lock is lifted and the fan is restored to its zone's current target.
Signed-off-by: Mike Capps <mikepcapps@gmail.com>
Change-Id: I6bf5bd55fd6eac2131aec02775a64ad287d3b3b0
diff --git a/control/json/actions/override_fan_target.cpp b/control/json/actions/override_fan_target.cpp
new file mode 100644
index 0000000..e8ce823
--- /dev/null
+++ b/control/json/actions/override_fan_target.cpp
@@ -0,0 +1,152 @@
+/**
+ * Copyright © 2022 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 "override_fan_target.hpp"
+
+#include "../manager.hpp"
+#include "../zone.hpp"
+#include "action.hpp"
+#include "group.hpp"
+
+#include <fmt/format.h>
+
+#include <nlohmann/json.hpp>
+
+namespace phosphor::fan::control::json
+{
+
+using json = nlohmann::json;
+
+OverrideFanTarget::OverrideFanTarget(const json& jsonObj,
+ const std::vector<Group>& groups) :
+ ActionBase(jsonObj, groups)
+{
+ setCount(jsonObj);
+ setState(jsonObj);
+ setTarget(jsonObj);
+ setFans(jsonObj);
+}
+
+void OverrideFanTarget::run(Zone& zone)
+{
+ size_t numAtState = 0;
+
+ for (const auto& group : _groups)
+ {
+ for (const auto& member : group.getMembers())
+ {
+ try
+ {
+ if (Manager::getObjValueVariant(member, group.getInterface(),
+ group.getProperty()) == _state)
+ {
+ numAtState++;
+
+ if (numAtState >= _count)
+ {
+ break;
+ }
+ }
+ }
+ catch (const std::out_of_range&)
+ {}
+ }
+
+ // lock the fans
+ if (numAtState >= _count)
+ {
+ lockFans(zone);
+ break;
+ }
+ }
+
+ if (_locked && numAtState < _count)
+ {
+ unlockFans(zone);
+ }
+}
+
+void OverrideFanTarget::lockFans(Zone& zone)
+{
+ if (!_locked)
+ {
+ record("Adding fan target lock of " + std::to_string(_target) +
+ " on zone " + zone.getName());
+
+ for (auto& fan : _fans)
+ {
+ zone.lockFanTarget(fan, _target);
+ }
+
+ _locked = true;
+ }
+}
+
+void OverrideFanTarget::unlockFans(Zone& zone)
+{
+ record("Un-locking fan target " + std::to_string(_target) + " on zone " +
+ zone.getName());
+
+ // unlock all fans in this instance
+ for (auto& fan : _fans)
+ {
+ zone.unlockFanTarget(fan, _target);
+ }
+
+ _locked = false;
+}
+
+void OverrideFanTarget::setCount(const json& jsonObj)
+{
+ if (!jsonObj.contains("count"))
+ {
+ throw ActionParseError{ActionBase::getName(),
+ "Missing required count value"};
+ }
+ _count = jsonObj["count"].get<size_t>();
+}
+
+void OverrideFanTarget::setState(const json& jsonObj)
+{
+ if (!jsonObj.contains("state"))
+ {
+ throw ActionParseError{ActionBase::getName(),
+ "Missing required state value"};
+ }
+ _state = getJsonValue(jsonObj["state"]);
+}
+
+void OverrideFanTarget::setTarget(const json& jsonObj)
+{
+ if (!jsonObj.contains("target"))
+ {
+ throw ActionParseError{ActionBase::getName(),
+ "Missing required target value"};
+ }
+ _target = jsonObj["target"].get<uint64_t>();
+}
+
+void OverrideFanTarget::setFans(const json& jsonObj)
+{
+ if (!jsonObj.contains("fans"))
+ {
+ throw ActionParseError{ActionBase::getName(),
+ "Missing required fans value"};
+ }
+
+ _fans = jsonObj["fans"].get<std::vector<std::string>>();
+}
+
+} // namespace phosphor::fan::control::json