1 /**
2  * Copyright © 2021 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "net_target_decrease.hpp"
17 
18 #include "../manager.hpp"
19 #include "../zone.hpp"
20 #include "action.hpp"
21 #include "group.hpp"
22 
23 #include <fmt/format.h>
24 
25 #include <nlohmann/json.hpp>
26 #include <phosphor-logging/log.hpp>
27 
28 #include <algorithm>
29 #include <variant>
30 
31 namespace phosphor::fan::control::json
32 {
33 
34 using json = nlohmann::json;
35 using namespace phosphor::logging;
36 
37 NetTargetDecrease::NetTargetDecrease(const json& jsonObj,
38                                      const std::vector<Group>& groups) :
39     ActionBase(jsonObj, groups)
40 {
41     setState(jsonObj);
42     setDelta(jsonObj);
43 }
44 
45 void NetTargetDecrease::run(Zone& zone)
46 {
47     if (!_stateParameter.empty())
48     {
49         auto s = Manager::getParameter(_stateParameter);
50         if (!s)
51         {
52             return;
53         }
54         _state = *s;
55     }
56 
57     auto netDelta = zone.getDecDelta();
58     for (const auto& group : _groups)
59     {
60         for (const auto& member : group.getMembers())
61         {
62             try
63             {
64                 auto value = Manager::getObjValueVariant(
65                     member, group.getInterface(), group.getProperty());
66                 if (std::holds_alternative<int64_t>(value) ||
67                     std::holds_alternative<double>(value))
68                 {
69                     if (value >= _state)
70                     {
71                         // No decrease allowed for this group
72                         netDelta = 0;
73                         break;
74                     }
75                     else
76                     {
77                         // Decrease factor is the difference in configured state
78                         // to the current value's state
79                         uint64_t deltaFactor = 0;
80                         if (auto dblPtr = std::get_if<double>(&value))
81                         {
82                             deltaFactor = static_cast<uint64_t>(
83                                 std::get<double>(_state) - *dblPtr);
84                         }
85                         else
86                         {
87                             deltaFactor = static_cast<uint64_t>(
88                                 std::get<int64_t>(_state) -
89                                 std::get<int64_t>(value));
90                         }
91 
92                         // Multiply the decrease factor by the configured delta
93                         // to get the net decrease delta for the given group
94                         // member. The lowest net decrease delta of the entire
95                         // group is the decrease requested.
96                         if (netDelta == 0)
97                         {
98                             netDelta = deltaFactor * _delta;
99                         }
100                         else
101                         {
102                             netDelta = std::min(netDelta, deltaFactor * _delta);
103                         }
104                     }
105                 }
106                 else if (std::holds_alternative<bool>(value) ||
107                          std::holds_alternative<std::string>(value))
108                 {
109                     // Where a group of booleans or strings equal the state
110                     // provided, request a decrease of the configured delta
111                     if (_state == value)
112                     {
113                         if (netDelta == 0)
114                         {
115                             netDelta = _delta;
116                         }
117                         else
118                         {
119                             netDelta = std::min(netDelta, _delta);
120                         }
121                     }
122                 }
123                 else
124                 {
125                     // Unsupported group member type for this action
126                     log<level::ERR>(
127                         fmt::format("Action {}: Unsupported group member type "
128                                     "given. [object = {} : {} : {}]",
129                                     ActionBase::getName(), member,
130                                     group.getInterface(), group.getProperty())
131                             .c_str());
132                 }
133             }
134             catch (const std::out_of_range& oore)
135             {
136                 // Property value not found, netDelta unchanged
137             }
138         }
139         // Update group's decrease allowed state
140         zone.setDecreaseAllow(group.getName(), !(netDelta == 0));
141     }
142     // Request target decrease to occur on decrease interval
143     zone.requestDecrease(netDelta);
144 }
145 
146 void NetTargetDecrease::setState(const json& jsonObj)
147 {
148     if (jsonObj.contains("state"))
149     {
150         _state = getJsonValue(jsonObj["state"]);
151     }
152     else if (jsonObj.contains("state_parameter_name"))
153     {
154         _stateParameter = jsonObj["state_parameter_name"].get<std::string>();
155     }
156     else
157     {
158         throw ActionParseError{
159             ActionBase::getName(),
160             "Missing required state or state_parameter_name value"};
161     }
162 }
163 
164 void NetTargetDecrease::setDelta(const json& jsonObj)
165 {
166     if (!jsonObj.contains("delta"))
167     {
168         throw ActionParseError{ActionBase::getName(),
169                                "Missing required delta value"};
170     }
171     _delta = jsonObj["delta"].get<uint64_t>();
172 }
173 
174 } // namespace phosphor::fan::control::json
175