blob: 6805a2d4005ed70f912b11c898eacafdc7a82e8f [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"
George Liu7a1dbc42022-12-07 16:03:22 +080019#include "dbus_utility.hpp"
James Feistb49ac872019-05-21 15:12:01 -070020
John Edward Broadbent7e860f12021-04-08 15:57:16 -070021#include <app.hpp>
James Feistb49ac872019-05-21 15:12:01 -070022#include <dbus_singleton.hpp>
Nan Zhoudfababf2022-05-17 23:12:06 +000023#include <nlohmann/json.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024
George Liu7a1dbc42022-12-07 16:03:22 +080025#include <array>
26#include <string_view>
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|.
Ed Tanous4e23a442022-06-06 09:57:26 -070035 explicit HealthPopulate(
36 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
37 asyncResp(asyncRespIn),
38 statusPtr("/Status")
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039 {}
James Feist5bc2dc82019-10-22 14:33:16 -070040
Nan Zhoudfababf2022-05-17 23:12:06 +000041 // Takes a JSON pointer rather than a reference. This is pretty useful when
42 // the address of the status JSON might change, for example, elements in an
43 // array.
zhanghch058d1b46d2021-04-01 11:18:24 +080044 HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
Nan Zhoudfababf2022-05-17 23:12:06 +000045 const nlohmann::json::json_pointer& ptr) :
Ed Tanous23a21a12020-07-25 04:45:05 +000046 asyncResp(asyncRespIn),
Nan Zhoudfababf2022-05-17 23:12:06 +000047 statusPtr(ptr)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050048 {}
James Feistb49ac872019-05-21 15:12:01 -070049
Ed Tanousecd6a3a2022-01-07 09:18:40 -080050 HealthPopulate(const HealthPopulate&) = delete;
51 HealthPopulate(HealthPopulate&&) = delete;
52 HealthPopulate& operator=(const HealthPopulate&) = delete;
53 HealthPopulate& operator=(const HealthPopulate&&) = delete;
54
James Feistb49ac872019-05-21 15:12:01 -070055 ~HealthPopulate()
56 {
Nan Zhoudfababf2022-05-17 23:12:06 +000057 nlohmann::json& jsonStatus = asyncResp->res.jsonValue[statusPtr];
Gunnar Mills1214b7e2020-06-04 10:11:30 -050058 nlohmann::json& health = jsonStatus["Health"];
59 nlohmann::json& rollup = jsonStatus["HealthRollup"];
James Feistb49ac872019-05-21 15:12:01 -070060
61 health = "OK";
62 rollup = "OK";
63
Ed Tanous23a21a12020-07-25 04:45:05 +000064 for (const std::shared_ptr<HealthPopulate>& healthChild : children)
James Feist5bc2dc82019-10-22 14:33:16 -070065 {
Ed Tanous23a21a12020-07-25 04:45:05 +000066 healthChild->globalInventoryPath = globalInventoryPath;
67 healthChild->statuses = statuses;
James Feist5bc2dc82019-10-22 14:33:16 -070068 }
69
Gunnar Mills1214b7e2020-06-04 10:11:30 -050070 for (const auto& [path, interfaces] : statuses)
James Feistb49ac872019-05-21 15:12:01 -070071 {
Karol Wojciechowski42cbb512021-07-28 17:12:20 +020072 bool isSelf = false;
73 if (selfPath)
74 {
75 if (boost::equals(path.str, *selfPath) ||
Ed Tanous11ba3972022-07-11 09:50:41 -070076 path.str.starts_with(*selfPath + "/"))
Karol Wojciechowski42cbb512021-07-28 17:12:20 +020077 {
78 isSelf = true;
79 }
80 }
James Feistb49ac872019-05-21 15:12:01 -070081
82 // managers inventory is all the inventory, don't skip any
James Feist35e257a2020-06-05 13:30:51 -070083 if (!isManagersHealth && !isSelf)
James Feistb49ac872019-05-21 15:12:01 -070084 {
85
86 // We only want to look at this association if either the path
87 // of this association is an inventory item, or one of the
88 // endpoints in this association is a child
89
Ed Tanousf8fe53e2022-06-30 15:55:45 -070090 bool isChild = false;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050091 for (const std::string& child : inventory)
James Feistb49ac872019-05-21 15:12:01 -070092 {
Ed Tanous11ba3972022-07-11 09:50:41 -070093 if (path.str.starts_with(child))
James Feistb49ac872019-05-21 15:12:01 -070094 {
95 isChild = true;
96 break;
97 }
98 }
99 if (!isChild)
100 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800101 for (const auto& [interface, association] : interfaces)
James Feistb49ac872019-05-21 15:12:01 -0700102 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800103 if (interface != "xyz.openbmc_project.Association")
James Feistb49ac872019-05-21 15:12:01 -0700104 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800105 continue;
James Feistb49ac872019-05-21 15:12:01 -0700106 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800107 for (const auto& [name, value] : association)
108 {
109 if (name != "endpoints")
110 {
111 continue;
112 }
113
114 const std::vector<std::string>* endpoints =
115 std::get_if<std::vector<std::string>>(&value);
116 if (endpoints == nullptr)
117 {
118 BMCWEB_LOG_ERROR << "Illegal association at "
119 << path.str;
120 continue;
121 }
122 bool containsChild = false;
123 for (const std::string& endpoint : *endpoints)
124 {
125 if (std::find(inventory.begin(),
126 inventory.end(),
127 endpoint) != inventory.end())
128 {
129 containsChild = true;
130 break;
131 }
132 }
133 if (!containsChild)
134 {
135 continue;
136 }
137 }
James Feistb49ac872019-05-21 15:12:01 -0700138 }
139 }
140 }
141
Ed Tanous11ba3972022-07-11 09:50:41 -0700142 if (path.str.starts_with(globalInventoryPath) &&
143 path.str.ends_with("critical"))
James Feistb49ac872019-05-21 15:12:01 -0700144 {
145 health = "Critical";
146 rollup = "Critical";
147 return;
148 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700149 if (path.str.starts_with(globalInventoryPath) &&
150 path.str.ends_with("warning"))
James Feistb49ac872019-05-21 15:12:01 -0700151 {
152 health = "Warning";
153 if (rollup != "Critical")
154 {
155 rollup = "Warning";
156 }
157 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700158 else if (path.str.ends_with("critical"))
James Feistb49ac872019-05-21 15:12:01 -0700159 {
160 rollup = "Critical";
James Feist35e257a2020-06-05 13:30:51 -0700161 if (isSelf)
162 {
163 health = "Critical";
164 return;
165 }
James Feistb49ac872019-05-21 15:12:01 -0700166 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700167 else if (path.str.ends_with("warning"))
James Feistb49ac872019-05-21 15:12:01 -0700168 {
169 if (rollup != "Critical")
170 {
171 rollup = "Warning";
172 }
James Feist35e257a2020-06-05 13:30:51 -0700173
174 if (isSelf)
175 {
176 health = "Warning";
177 }
James Feistb49ac872019-05-21 15:12:01 -0700178 }
179 }
180 }
181
James Feist5bc2dc82019-10-22 14:33:16 -0700182 // this should only be called once per url, others should get updated by
183 // being added as children to the 'main' health object for the page
James Feistb49ac872019-05-21 15:12:01 -0700184 void populate()
185 {
James Feist9536a142019-11-21 09:02:32 -0800186 if (populated)
187 {
188 return;
189 }
190 populated = true;
James Feistb49ac872019-05-21 15:12:01 -0700191 getAllStatusAssociations();
192 getGlobalPath();
193 }
194
195 void getGlobalPath()
196 {
George Liu7a1dbc42022-12-07 16:03:22 +0800197 constexpr std::array<std::string_view, 1> interfaces = {
198 "xyz.openbmc_project.Inventory.Item.Global"};
James Feistb49ac872019-05-21 15:12:01 -0700199 std::shared_ptr<HealthPopulate> self = shared_from_this();
George Liu7a1dbc42022-12-07 16:03:22 +0800200 dbus::utility::getSubTreePaths(
201 "/", 0, interfaces,
202 [self](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800203 const dbus::utility::MapperGetSubTreePathsResponse& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700204 if (ec || resp.size() != 1)
205 {
206 // no global item, or too many
207 return;
208 }
209 self->globalInventoryPath = resp[0];
George Liu7a1dbc42022-12-07 16:03:22 +0800210 });
James Feistb49ac872019-05-21 15:12:01 -0700211 }
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) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700219 if (ec)
220 {
221 return;
222 }
223 self->statuses = resp;
224 for (auto it = self->statuses.begin(); it != self->statuses.end();)
225 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700226 if (it->first.str.ends_with("critical") ||
227 it->first.str.ends_with("warning"))
James Feistb49ac872019-05-21 15:12:01 -0700228 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700229 it++;
230 continue;
James Feistb49ac872019-05-21 15:12:01 -0700231 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700232 it = self->statuses.erase(it);
233 }
James Feistb49ac872019-05-21 15:12:01 -0700234 },
235 "xyz.openbmc_project.ObjectMapper", "/",
236 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
237 }
238
zhanghch058d1b46d2021-04-01 11:18:24 +0800239 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Nan Zhoudfababf2022-05-17 23:12:06 +0000240
241 // Will populate the health status into |asyncResp_json[statusPtr]|
242 nlohmann::json::json_pointer statusPtr;
James Feist5bc2dc82019-10-22 14:33:16 -0700243
244 // we store pointers to other HealthPopulate items so we can update their
245 // members and reduce dbus calls. As we hold a shared_ptr to them, they get
246 // destroyed last, and they need not call populate()
247 std::vector<std::shared_ptr<HealthPopulate>> children;
248
James Feist35e257a2020-06-05 13:30:51 -0700249 // self is used if health is for an individual items status, as this is the
250 // 'lowest most' item, the rollup will equal the health
251 std::optional<std::string> selfPath;
252
James Feistb49ac872019-05-21 15:12:01 -0700253 std::vector<std::string> inventory;
254 bool isManagersHealth = false;
255 dbus::utility::ManagedObjectType statuses;
256 std::string globalInventoryPath = "-"; // default to illegal dbus path
James Feist9536a142019-11-21 09:02:32 -0800257 bool populated = false;
James Feistb49ac872019-05-21 15:12:01 -0700258};
Ed Tanous23a21a12020-07-25 04:45:05 +0000259} // namespace redfish