blob: 31adf7b431e28fdc1cf48c73d89e9ea3aa6e3a21 [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>
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{
zhanghch058d1b46d2021-04-01 11:18:24 +080033 HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Ed Tanous23a21a12020-07-25 04:45:05 +000034 asyncResp(asyncRespIn), jsonStatus(asyncResp->res.jsonValue["Status"])
Gunnar Mills1214b7e2020-06-04 10:11:30 -050035 {}
James Feist5bc2dc82019-10-22 14:33:16 -070036
zhanghch058d1b46d2021-04-01 11:18:24 +080037 HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038 nlohmann::json& status) :
Ed Tanous23a21a12020-07-25 04:45:05 +000039 asyncResp(asyncRespIn),
James Feist5bc2dc82019-10-22 14:33:16 -070040 jsonStatus(status)
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041 {}
James Feistb49ac872019-05-21 15:12:01 -070042
Ed Tanousecd6a3a2022-01-07 09:18:40 -080043 HealthPopulate(const HealthPopulate&) = delete;
44 HealthPopulate(HealthPopulate&&) = delete;
45 HealthPopulate& operator=(const HealthPopulate&) = delete;
46 HealthPopulate& operator=(const HealthPopulate&&) = delete;
47
James Feistb49ac872019-05-21 15:12:01 -070048 ~HealthPopulate()
49 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -050050 nlohmann::json& health = jsonStatus["Health"];
51 nlohmann::json& rollup = jsonStatus["HealthRollup"];
James Feistb49ac872019-05-21 15:12:01 -070052
53 health = "OK";
54 rollup = "OK";
55
Ed Tanous23a21a12020-07-25 04:45:05 +000056 for (const std::shared_ptr<HealthPopulate>& healthChild : children)
James Feist5bc2dc82019-10-22 14:33:16 -070057 {
Ed Tanous23a21a12020-07-25 04:45:05 +000058 healthChild->globalInventoryPath = globalInventoryPath;
59 healthChild->statuses = statuses;
James Feist5bc2dc82019-10-22 14:33:16 -070060 }
61
Gunnar Mills1214b7e2020-06-04 10:11:30 -050062 for (const auto& [path, interfaces] : statuses)
James Feistb49ac872019-05-21 15:12:01 -070063 {
64 bool isChild = false;
Karol Wojciechowski42cbb512021-07-28 17:12:20 +020065 bool isSelf = false;
66 if (selfPath)
67 {
68 if (boost::equals(path.str, *selfPath) ||
69 boost::starts_with(path.str, *selfPath + "/"))
70 {
71 isSelf = true;
72 }
73 }
James Feistb49ac872019-05-21 15:12:01 -070074
75 // managers inventory is all the inventory, don't skip any
James Feist35e257a2020-06-05 13:30:51 -070076 if (!isManagersHealth && !isSelf)
James Feistb49ac872019-05-21 15:12:01 -070077 {
78
79 // We only want to look at this association if either the path
80 // of this association is an inventory item, or one of the
81 // endpoints in this association is a child
82
Gunnar Mills1214b7e2020-06-04 10:11:30 -050083 for (const std::string& child : inventory)
James Feistb49ac872019-05-21 15:12:01 -070084 {
85 if (boost::starts_with(path.str, child))
86 {
87 isChild = true;
88 break;
89 }
90 }
91 if (!isChild)
92 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080093 for (const auto& [interface, association] : interfaces)
James Feistb49ac872019-05-21 15:12:01 -070094 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080095 if (interface != "xyz.openbmc_project.Association")
James Feistb49ac872019-05-21 15:12:01 -070096 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080097 continue;
James Feistb49ac872019-05-21 15:12:01 -070098 }
Ed Tanous711ac7a2021-12-20 09:34:41 -080099 for (const auto& [name, value] : association)
100 {
101 if (name != "endpoints")
102 {
103 continue;
104 }
105
106 const std::vector<std::string>* endpoints =
107 std::get_if<std::vector<std::string>>(&value);
108 if (endpoints == nullptr)
109 {
110 BMCWEB_LOG_ERROR << "Illegal association at "
111 << path.str;
112 continue;
113 }
114 bool containsChild = false;
115 for (const std::string& endpoint : *endpoints)
116 {
117 if (std::find(inventory.begin(),
118 inventory.end(),
119 endpoint) != inventory.end())
120 {
121 containsChild = true;
122 break;
123 }
124 }
125 if (!containsChild)
126 {
127 continue;
128 }
129 }
James Feistb49ac872019-05-21 15:12:01 -0700130 }
131 }
132 }
133
134 if (boost::starts_with(path.str, globalInventoryPath) &&
135 boost::ends_with(path.str, "critical"))
136 {
137 health = "Critical";
138 rollup = "Critical";
139 return;
140 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700141 if (boost::starts_with(path.str, globalInventoryPath) &&
142 boost::ends_with(path.str, "warning"))
James Feistb49ac872019-05-21 15:12:01 -0700143 {
144 health = "Warning";
145 if (rollup != "Critical")
146 {
147 rollup = "Warning";
148 }
149 }
150 else if (boost::ends_with(path.str, "critical"))
151 {
152 rollup = "Critical";
James Feist35e257a2020-06-05 13:30:51 -0700153 if (isSelf)
154 {
155 health = "Critical";
156 return;
157 }
James Feistb49ac872019-05-21 15:12:01 -0700158 }
159 else if (boost::ends_with(path.str, "warning"))
160 {
161 if (rollup != "Critical")
162 {
163 rollup = "Warning";
164 }
James Feist35e257a2020-06-05 13:30:51 -0700165
166 if (isSelf)
167 {
168 health = "Warning";
169 }
James Feistb49ac872019-05-21 15:12:01 -0700170 }
171 }
172 }
173
James Feist5bc2dc82019-10-22 14:33:16 -0700174 // this should only be called once per url, others should get updated by
175 // being added as children to the 'main' health object for the page
James Feistb49ac872019-05-21 15:12:01 -0700176 void populate()
177 {
James Feist9536a142019-11-21 09:02:32 -0800178 if (populated)
179 {
180 return;
181 }
182 populated = true;
James Feistb49ac872019-05-21 15:12:01 -0700183 getAllStatusAssociations();
184 getGlobalPath();
185 }
186
187 void getGlobalPath()
188 {
189 std::shared_ptr<HealthPopulate> self = shared_from_this();
190 crow::connections::systemBus->async_method_call(
191 [self](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800192 const dbus::utility::MapperGetSubTreePathsResponse& resp) {
James Feistb49ac872019-05-21 15:12:01 -0700193 if (ec || resp.size() != 1)
194 {
195 // no global item, or too many
196 return;
197 }
Ed Tanous914e2d52022-01-07 11:38:34 -0800198 self->globalInventoryPath = resp[0];
James Feistb49ac872019-05-21 15:12:01 -0700199 },
200 "xyz.openbmc_project.ObjectMapper",
201 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700202 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203 std::array<const char*, 1>{
James Feistb49ac872019-05-21 15:12:01 -0700204 "xyz.openbmc_project.Inventory.Item.Global"});
205 }
206
207 void getAllStatusAssociations()
208 {
209 std::shared_ptr<HealthPopulate> self = shared_from_this();
210 crow::connections::systemBus->async_method_call(
211 [self](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -0800212 const dbus::utility::ManagedObjectType& resp) {
James Feistb49ac872019-05-21 15:12:01 -0700213 if (ec)
214 {
215 return;
216 }
Ed Tanouscd5e4302022-01-11 12:42:34 -0800217 self->statuses = resp;
218 for (auto it = self->statuses.begin();
219 it != self->statuses.end();)
James Feistb49ac872019-05-21 15:12:01 -0700220 {
221 if (boost::ends_with(it->first.str, "critical") ||
222 boost::ends_with(it->first.str, "warning"))
223 {
224 it++;
225 continue;
226 }
Ed Tanouscd5e4302022-01-11 12:42:34 -0800227 it = self->statuses.erase(it);
James Feistb49ac872019-05-21 15:12:01 -0700228 }
James Feistb49ac872019-05-21 15:12:01 -0700229 },
230 "xyz.openbmc_project.ObjectMapper", "/",
231 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
232 }
233
zhanghch058d1b46d2021-04-01 11:18:24 +0800234 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500235 nlohmann::json& jsonStatus;
James Feist5bc2dc82019-10-22 14:33:16 -0700236
237 // we store pointers to other HealthPopulate items so we can update their
238 // members and reduce dbus calls. As we hold a shared_ptr to them, they get
239 // destroyed last, and they need not call populate()
240 std::vector<std::shared_ptr<HealthPopulate>> children;
241
James Feist35e257a2020-06-05 13:30:51 -0700242 // self is used if health is for an individual items status, as this is the
243 // 'lowest most' item, the rollup will equal the health
244 std::optional<std::string> selfPath;
245
James Feistb49ac872019-05-21 15:12:01 -0700246 std::vector<std::string> inventory;
247 bool isManagersHealth = false;
248 dbus::utility::ManagedObjectType statuses;
249 std::string globalInventoryPath = "-"; // default to illegal dbus path
James Feist9536a142019-11-21 09:02:32 -0800250 bool populated = false;
James Feistb49ac872019-05-21 15:12:01 -0700251};
Ed Tanous23a21a12020-07-25 04:45:05 +0000252} // namespace redfish