blob: 04281bf90384de41e61539ee7f8005354a21abb3 [file] [log] [blame]
James Feistb49ac872019-05-21 15:12:01 -07001/*
2// Copyright (c) 2019 Intel 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#pragma once
17
18#include "async_resp.hpp"
19
John Edward Broadbent7e860f12021-04-08 15:57:16 -070020#include <app.hpp>
James Feistb49ac872019-05-21 15:12:01 -070021#include <boost/algorithm/string/predicate.hpp>
James Feistb49ac872019-05-21 15:12:01 -070022#include <dbus_singleton.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070023#include <dbus_utility.hpp>
Nan Zhoudfababf2022-05-17 23:12:06 +000024#include <nlohmann/json.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025
James Feistb49ac872019-05-21 15:12:01 -070026#include <variant>
27
28namespace redfish
29{
30
31struct HealthPopulate : std::enable_shared_from_this<HealthPopulate>
32{
Nan Zhoudfababf2022-05-17 23:12:06 +000033 // By default populate status to "/Status" of |asyncResp->res.jsonValue|.
zhanghch058d1b46d2021-04-01 11:18:24 +080034 HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Nan Zhoudfababf2022-05-17 23:12:06 +000035 asyncResp(asyncRespIn), statusPtr("/Status")
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036 {}
James Feist5bc2dc82019-10-22 14:33:16 -070037
Nan Zhoudfababf2022-05-17 23:12:06 +000038 // Takes a JSON pointer rather than a reference. This is pretty useful when
39 // the address of the status JSON might change, for example, elements in an
40 // array.
zhanghch058d1b46d2021-04-01 11:18:24 +080041 HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
Nan Zhoudfababf2022-05-17 23:12:06 +000042 const nlohmann::json::json_pointer& ptr) :
Ed Tanous23a21a12020-07-25 04:45:05 +000043 asyncResp(asyncRespIn),
Nan Zhoudfababf2022-05-17 23:12:06 +000044 statusPtr(ptr)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050045 {}
James Feistb49ac872019-05-21 15:12:01 -070046
Ed Tanousecd6a3a2022-01-07 09:18:40 -080047 HealthPopulate(const HealthPopulate&) = delete;
48 HealthPopulate(HealthPopulate&&) = delete;
49 HealthPopulate& operator=(const HealthPopulate&) = delete;
50 HealthPopulate& operator=(const HealthPopulate&&) = delete;
51
James Feistb49ac872019-05-21 15:12:01 -070052 ~HealthPopulate()
53 {
Nan Zhoudfababf2022-05-17 23:12:06 +000054 nlohmann::json& jsonStatus = asyncResp->res.jsonValue[statusPtr];
Gunnar Mills1214b7e2020-06-04 10:11:30 -050055 nlohmann::json& health = jsonStatus["Health"];
56 nlohmann::json& rollup = jsonStatus["HealthRollup"];
James Feistb49ac872019-05-21 15:12:01 -070057
58 health = "OK";
59 rollup = "OK";
60
Ed Tanous23a21a12020-07-25 04:45:05 +000061 for (const std::shared_ptr<HealthPopulate>& healthChild : children)
James Feist5bc2dc82019-10-22 14:33:16 -070062 {
Ed Tanous23a21a12020-07-25 04:45:05 +000063 healthChild->globalInventoryPath = globalInventoryPath;
64 healthChild->statuses = statuses;
James Feist5bc2dc82019-10-22 14:33:16 -070065 }
66
Gunnar Mills1214b7e2020-06-04 10:11:30 -050067 for (const auto& [path, interfaces] : statuses)
James Feistb49ac872019-05-21 15:12:01 -070068 {
69 bool isChild = false;
Karol Wojciechowski42cbb512021-07-28 17:12:20 +020070 bool isSelf = false;
71 if (selfPath)
72 {
73 if (boost::equals(path.str, *selfPath) ||
74 boost::starts_with(path.str, *selfPath + "/"))
75 {
76 isSelf = true;
77 }
78 }
James Feistb49ac872019-05-21 15:12:01 -070079
80 // managers inventory is all the inventory, don't skip any
James Feist35e257a2020-06-05 13:30:51 -070081 if (!isManagersHealth && !isSelf)
James Feistb49ac872019-05-21 15:12:01 -070082 {
83
84 // We only want to look at this association if either the path
85 // of this association is an inventory item, or one of the
86 // endpoints in this association is a child
87
Gunnar Mills1214b7e2020-06-04 10:11:30 -050088 for (const std::string& child : inventory)
James Feistb49ac872019-05-21 15:12:01 -070089 {
90 if (boost::starts_with(path.str, child))
91 {
92 isChild = true;
93 break;
94 }
95 }
96 if (!isChild)
97 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080098 for (const auto& [interface, association] : interfaces)
James Feistb49ac872019-05-21 15:12:01 -070099 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800100 if (interface != "xyz.openbmc_project.Association")
James Feistb49ac872019-05-21 15:12:01 -0700101 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800102 continue;
James Feistb49ac872019-05-21 15:12:01 -0700103 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800104 for (const auto& [name, value] : association)
105 {
106 if (name != "endpoints")
107 {
108 continue;
109 }
110
111 const std::vector<std::string>* endpoints =
112 std::get_if<std::vector<std::string>>(&value);
113 if (endpoints == nullptr)
114 {
115 BMCWEB_LOG_ERROR << "Illegal association at "
116 << path.str;
117 continue;
118 }
119 bool containsChild = false;
120 for (const std::string& endpoint : *endpoints)
121 {
122 if (std::find(inventory.begin(),
123 inventory.end(),
124 endpoint) != inventory.end())
125 {
126 containsChild = true;
127 break;
128 }
129 }
130 if (!containsChild)
131 {
132 continue;
133 }
134 }
James Feistb49ac872019-05-21 15:12:01 -0700135 }
136 }
137 }
138
139 if (boost::starts_with(path.str, globalInventoryPath) &&
140 boost::ends_with(path.str, "critical"))
141 {
142 health = "Critical";
143 rollup = "Critical";
144 return;
145 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700146 if (boost::starts_with(path.str, globalInventoryPath) &&
147 boost::ends_with(path.str, "warning"))
James Feistb49ac872019-05-21 15:12:01 -0700148 {
149 health = "Warning";
150 if (rollup != "Critical")
151 {
152 rollup = "Warning";
153 }
154 }
155 else if (boost::ends_with(path.str, "critical"))
156 {
157 rollup = "Critical";
James Feist35e257a2020-06-05 13:30:51 -0700158 if (isSelf)
159 {
160 health = "Critical";
161 return;
162 }
James Feistb49ac872019-05-21 15:12:01 -0700163 }
164 else if (boost::ends_with(path.str, "warning"))
165 {
166 if (rollup != "Critical")
167 {
168 rollup = "Warning";
169 }
James Feist35e257a2020-06-05 13:30:51 -0700170
171 if (isSelf)
172 {
173 health = "Warning";
174 }
James Feistb49ac872019-05-21 15:12:01 -0700175 }
176 }
177 }
178
James Feist5bc2dc82019-10-22 14:33:16 -0700179 // this should only be called once per url, others should get updated by
180 // being added as children to the 'main' health object for the page
James Feistb49ac872019-05-21 15:12:01 -0700181 void populate()
182 {
James Feist9536a142019-11-21 09:02:32 -0800183 if (populated)
184 {
185 return;
186 }
187 populated = true;
James Feistb49ac872019-05-21 15:12:01 -0700188 getAllStatusAssociations();
189 getGlobalPath();
190 }
191
192 void getGlobalPath()
193 {
194 std::shared_ptr<HealthPopulate> self = shared_from_this();
195 crow::connections::systemBus->async_method_call(
196 [self](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800197 const dbus::utility::MapperGetSubTreePathsResponse& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700198 if (ec || resp.size() != 1)
199 {
200 // no global item, or too many
201 return;
202 }
203 self->globalInventoryPath = resp[0];
James Feistb49ac872019-05-21 15:12:01 -0700204 },
205 "xyz.openbmc_project.ObjectMapper",
206 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700207 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500208 std::array<const char*, 1>{
James Feistb49ac872019-05-21 15:12:01 -0700209 "xyz.openbmc_project.Inventory.Item.Global"});
210 }
211
212 void getAllStatusAssociations()
213 {
214 std::shared_ptr<HealthPopulate> self = shared_from_this();
215 crow::connections::systemBus->async_method_call(
216 [self](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -0800217 const dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700218 if (ec)
219 {
220 return;
221 }
222 self->statuses = resp;
223 for (auto it = self->statuses.begin(); it != self->statuses.end();)
224 {
225 if (boost::ends_with(it->first.str, "critical") ||
226 boost::ends_with(it->first.str, "warning"))
James Feistb49ac872019-05-21 15:12:01 -0700227 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700228 it++;
229 continue;
James Feistb49ac872019-05-21 15:12:01 -0700230 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700231 it = self->statuses.erase(it);
232 }
James Feistb49ac872019-05-21 15:12:01 -0700233 },
234 "xyz.openbmc_project.ObjectMapper", "/",
235 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
236 }
237
zhanghch058d1b46d2021-04-01 11:18:24 +0800238 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Nan Zhoudfababf2022-05-17 23:12:06 +0000239
240 // Will populate the health status into |asyncResp_json[statusPtr]|
241 nlohmann::json::json_pointer statusPtr;
James Feist5bc2dc82019-10-22 14:33:16 -0700242
243 // we store pointers to other HealthPopulate items so we can update their
244 // members and reduce dbus calls. As we hold a shared_ptr to them, they get
245 // destroyed last, and they need not call populate()
246 std::vector<std::shared_ptr<HealthPopulate>> children;
247
James Feist35e257a2020-06-05 13:30:51 -0700248 // self is used if health is for an individual items status, as this is the
249 // 'lowest most' item, the rollup will equal the health
250 std::optional<std::string> selfPath;
251
James Feistb49ac872019-05-21 15:12:01 -0700252 std::vector<std::string> inventory;
253 bool isManagersHealth = false;
254 dbus::utility::ManagedObjectType statuses;
255 std::string globalInventoryPath = "-"; // default to illegal dbus path
James Feist9536a142019-11-21 09:02:32 -0800256 bool populated = false;
James Feistb49ac872019-05-21 15:12:01 -0700257};
Ed Tanous23a21a12020-07-25 04:45:05 +0000258} // namespace redfish