blob: 254f8e920364d088f819fd73e45f256959b1dd47 [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>
22#include <boost/container/flat_set.hpp>
23#include <dbus_singleton.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070024#include <dbus_utility.hpp>
Nan Zhoudfababf2022-05-17 23:12:06 +000025#include <nlohmann/json.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026
James Feistb49ac872019-05-21 15:12:01 -070027#include <variant>
28
29namespace redfish
30{
31
32struct HealthPopulate : std::enable_shared_from_this<HealthPopulate>
33{
Nan Zhoudfababf2022-05-17 23:12:06 +000034 // By default populate status to "/Status" of |asyncResp->res.jsonValue|.
zhanghch058d1b46d2021-04-01 11:18:24 +080035 HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Nan Zhoudfababf2022-05-17 23:12:06 +000036 asyncResp(asyncRespIn), statusPtr("/Status")
Gunnar Mills1214b7e2020-06-04 10:11:30 -050037 {}
James Feist5bc2dc82019-10-22 14:33:16 -070038
Nan Zhoudfababf2022-05-17 23:12:06 +000039 // Takes a JSON pointer rather than a reference. This is pretty useful when
40 // the address of the status JSON might change, for example, elements in an
41 // array.
zhanghch058d1b46d2021-04-01 11:18:24 +080042 HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
Nan Zhoudfababf2022-05-17 23:12:06 +000043 const nlohmann::json::json_pointer& ptr) :
Ed Tanous23a21a12020-07-25 04:45:05 +000044 asyncResp(asyncRespIn),
Nan Zhoudfababf2022-05-17 23:12:06 +000045 statusPtr(ptr)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046 {}
James Feistb49ac872019-05-21 15:12:01 -070047
Ed Tanousecd6a3a2022-01-07 09:18:40 -080048 HealthPopulate(const HealthPopulate&) = delete;
49 HealthPopulate(HealthPopulate&&) = delete;
50 HealthPopulate& operator=(const HealthPopulate&) = delete;
51 HealthPopulate& operator=(const HealthPopulate&&) = delete;
52
James Feistb49ac872019-05-21 15:12:01 -070053 ~HealthPopulate()
54 {
Nan Zhoudfababf2022-05-17 23:12:06 +000055 nlohmann::json& jsonStatus = asyncResp->res.jsonValue[statusPtr];
Gunnar Mills1214b7e2020-06-04 10:11:30 -050056 nlohmann::json& health = jsonStatus["Health"];
57 nlohmann::json& rollup = jsonStatus["HealthRollup"];
James Feistb49ac872019-05-21 15:12:01 -070058
59 health = "OK";
60 rollup = "OK";
61
Ed Tanous23a21a12020-07-25 04:45:05 +000062 for (const std::shared_ptr<HealthPopulate>& healthChild : children)
James Feist5bc2dc82019-10-22 14:33:16 -070063 {
Ed Tanous23a21a12020-07-25 04:45:05 +000064 healthChild->globalInventoryPath = globalInventoryPath;
65 healthChild->statuses = statuses;
James Feist5bc2dc82019-10-22 14:33:16 -070066 }
67
Gunnar Mills1214b7e2020-06-04 10:11:30 -050068 for (const auto& [path, interfaces] : statuses)
James Feistb49ac872019-05-21 15:12:01 -070069 {
70 bool isChild = false;
Karol Wojciechowski42cbb512021-07-28 17:12:20 +020071 bool isSelf = false;
72 if (selfPath)
73 {
74 if (boost::equals(path.str, *selfPath) ||
75 boost::starts_with(path.str, *selfPath + "/"))
76 {
77 isSelf = true;
78 }
79 }
James Feistb49ac872019-05-21 15:12:01 -070080
81 // managers inventory is all the inventory, don't skip any
James Feist35e257a2020-06-05 13:30:51 -070082 if (!isManagersHealth && !isSelf)
James Feistb49ac872019-05-21 15:12:01 -070083 {
84
85 // We only want to look at this association if either the path
86 // of this association is an inventory item, or one of the
87 // endpoints in this association is a child
88
Gunnar Mills1214b7e2020-06-04 10:11:30 -050089 for (const std::string& child : inventory)
James Feistb49ac872019-05-21 15:12:01 -070090 {
91 if (boost::starts_with(path.str, child))
92 {
93 isChild = true;
94 break;
95 }
96 }
97 if (!isChild)
98 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080099 for (const auto& [interface, association] : interfaces)
James Feistb49ac872019-05-21 15:12:01 -0700100 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800101 if (interface != "xyz.openbmc_project.Association")
James Feistb49ac872019-05-21 15:12:01 -0700102 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800103 continue;
James Feistb49ac872019-05-21 15:12:01 -0700104 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800105 for (const auto& [name, value] : association)
106 {
107 if (name != "endpoints")
108 {
109 continue;
110 }
111
112 const std::vector<std::string>* endpoints =
113 std::get_if<std::vector<std::string>>(&value);
114 if (endpoints == nullptr)
115 {
116 BMCWEB_LOG_ERROR << "Illegal association at "
117 << path.str;
118 continue;
119 }
120 bool containsChild = false;
121 for (const std::string& endpoint : *endpoints)
122 {
123 if (std::find(inventory.begin(),
124 inventory.end(),
125 endpoint) != inventory.end())
126 {
127 containsChild = true;
128 break;
129 }
130 }
131 if (!containsChild)
132 {
133 continue;
134 }
135 }
James Feistb49ac872019-05-21 15:12:01 -0700136 }
137 }
138 }
139
140 if (boost::starts_with(path.str, globalInventoryPath) &&
141 boost::ends_with(path.str, "critical"))
142 {
143 health = "Critical";
144 rollup = "Critical";
145 return;
146 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700147 if (boost::starts_with(path.str, globalInventoryPath) &&
148 boost::ends_with(path.str, "warning"))
James Feistb49ac872019-05-21 15:12:01 -0700149 {
150 health = "Warning";
151 if (rollup != "Critical")
152 {
153 rollup = "Warning";
154 }
155 }
156 else if (boost::ends_with(path.str, "critical"))
157 {
158 rollup = "Critical";
James Feist35e257a2020-06-05 13:30:51 -0700159 if (isSelf)
160 {
161 health = "Critical";
162 return;
163 }
James Feistb49ac872019-05-21 15:12:01 -0700164 }
165 else if (boost::ends_with(path.str, "warning"))
166 {
167 if (rollup != "Critical")
168 {
169 rollup = "Warning";
170 }
James Feist35e257a2020-06-05 13:30:51 -0700171
172 if (isSelf)
173 {
174 health = "Warning";
175 }
James Feistb49ac872019-05-21 15:12:01 -0700176 }
177 }
178 }
179
James Feist5bc2dc82019-10-22 14:33:16 -0700180 // this should only be called once per url, others should get updated by
181 // being added as children to the 'main' health object for the page
James Feistb49ac872019-05-21 15:12:01 -0700182 void populate()
183 {
James Feist9536a142019-11-21 09:02:32 -0800184 if (populated)
185 {
186 return;
187 }
188 populated = true;
James Feistb49ac872019-05-21 15:12:01 -0700189 getAllStatusAssociations();
190 getGlobalPath();
191 }
192
193 void getGlobalPath()
194 {
195 std::shared_ptr<HealthPopulate> self = shared_from_this();
196 crow::connections::systemBus->async_method_call(
197 [self](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800198 const dbus::utility::MapperGetSubTreePathsResponse& resp) {
James Feistb49ac872019-05-21 15:12:01 -0700199 if (ec || resp.size() != 1)
200 {
201 // no global item, or too many
202 return;
203 }
Ed Tanous914e2d52022-01-07 11:38:34 -0800204 self->globalInventoryPath = resp[0];
James Feistb49ac872019-05-21 15:12:01 -0700205 },
206 "xyz.openbmc_project.ObjectMapper",
207 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700208 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500209 std::array<const char*, 1>{
James Feistb49ac872019-05-21 15:12:01 -0700210 "xyz.openbmc_project.Inventory.Item.Global"});
211 }
212
213 void getAllStatusAssociations()
214 {
215 std::shared_ptr<HealthPopulate> self = shared_from_this();
216 crow::connections::systemBus->async_method_call(
217 [self](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -0800218 const dbus::utility::ManagedObjectType& resp) {
James Feistb49ac872019-05-21 15:12:01 -0700219 if (ec)
220 {
221 return;
222 }
Ed Tanouscd5e4302022-01-11 12:42:34 -0800223 self->statuses = resp;
224 for (auto it = self->statuses.begin();
225 it != self->statuses.end();)
James Feistb49ac872019-05-21 15:12:01 -0700226 {
227 if (boost::ends_with(it->first.str, "critical") ||
228 boost::ends_with(it->first.str, "warning"))
229 {
230 it++;
231 continue;
232 }
Ed Tanouscd5e4302022-01-11 12:42:34 -0800233 it = self->statuses.erase(it);
James Feistb49ac872019-05-21 15:12:01 -0700234 }
James Feistb49ac872019-05-21 15:12:01 -0700235 },
236 "xyz.openbmc_project.ObjectMapper", "/",
237 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
238 }
239
zhanghch058d1b46d2021-04-01 11:18:24 +0800240 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Nan Zhoudfababf2022-05-17 23:12:06 +0000241
242 // Will populate the health status into |asyncResp_json[statusPtr]|
243 nlohmann::json::json_pointer statusPtr;
James Feist5bc2dc82019-10-22 14:33:16 -0700244
245 // we store pointers to other HealthPopulate items so we can update their
246 // members and reduce dbus calls. As we hold a shared_ptr to them, they get
247 // destroyed last, and they need not call populate()
248 std::vector<std::shared_ptr<HealthPopulate>> children;
249
James Feist35e257a2020-06-05 13:30:51 -0700250 // self is used if health is for an individual items status, as this is the
251 // 'lowest most' item, the rollup will equal the health
252 std::optional<std::string> selfPath;
253
James Feistb49ac872019-05-21 15:12:01 -0700254 std::vector<std::string> inventory;
255 bool isManagersHealth = false;
256 dbus::utility::ManagedObjectType statuses;
257 std::string globalInventoryPath = "-"; // default to illegal dbus path
James Feist9536a142019-11-21 09:02:32 -0800258 bool populated = false;
James Feistb49ac872019-05-21 15:12:01 -0700259};
Ed Tanous23a21a12020-07-25 04:45:05 +0000260} // namespace redfish