blob: b9f2e38635b395fe0b67fe5160daeb1941de89d2 [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
20#include <boost/algorithm/string/predicate.hpp>
21#include <boost/container/flat_set.hpp>
22#include <dbus_singleton.hpp>
23#include <variant>
24
25namespace redfish
26{
27
28struct HealthPopulate : std::enable_shared_from_this<HealthPopulate>
29{
30 HealthPopulate(const std::shared_ptr<AsyncResp> &asyncResp) :
James Feist5bc2dc82019-10-22 14:33:16 -070031 asyncResp(asyncResp), jsonStatus(asyncResp->res.jsonValue["Status"])
32 {
33 }
34
35 HealthPopulate(const std::shared_ptr<AsyncResp> &asyncResp,
36 nlohmann::json &status) :
37 asyncResp(asyncResp),
38 jsonStatus(status)
James Feistb49ac872019-05-21 15:12:01 -070039 {
40 }
41
42 ~HealthPopulate()
43 {
James Feist5bc2dc82019-10-22 14:33:16 -070044 nlohmann::json &health = jsonStatus["Health"];
45 nlohmann::json &rollup = jsonStatus["HealthRollup"];
James Feistb49ac872019-05-21 15:12:01 -070046
47 health = "OK";
48 rollup = "OK";
49
James Feist5bc2dc82019-10-22 14:33:16 -070050 for (const std::shared_ptr<HealthPopulate> &health : children)
51 {
52 health->globalInventoryPath = globalInventoryPath;
53 health->statuses = statuses;
54 }
55
James Feistb49ac872019-05-21 15:12:01 -070056 for (const auto &[path, interfaces] : statuses)
57 {
58 bool isChild = false;
James Feist35e257a2020-06-05 13:30:51 -070059 bool isSelf =
60 selfPath ? boost::starts_with(path.str, *selfPath) : false;
James Feistb49ac872019-05-21 15:12:01 -070061
62 // managers inventory is all the inventory, don't skip any
James Feist35e257a2020-06-05 13:30:51 -070063 if (!isManagersHealth && !isSelf)
James Feistb49ac872019-05-21 15:12:01 -070064 {
65
66 // We only want to look at this association if either the path
67 // of this association is an inventory item, or one of the
68 // endpoints in this association is a child
69
70 for (const std::string &child : inventory)
71 {
72 if (boost::starts_with(path.str, child))
73 {
74 isChild = true;
75 break;
76 }
77 }
78 if (!isChild)
79 {
80 auto assocIt =
81 interfaces.find("xyz.openbmc_project.Association");
82 if (assocIt == interfaces.end())
83 {
84 continue;
85 }
86 auto endpointsIt = assocIt->second.find("endpoints");
87 if (endpointsIt == assocIt->second.end())
88 {
89 BMCWEB_LOG_ERROR << "Illegal association at "
90 << path.str;
91 continue;
92 }
93 const std::vector<std::string> *endpoints =
94 std::get_if<std::vector<std::string>>(
95 &endpointsIt->second);
96 if (endpoints == nullptr)
97 {
98 BMCWEB_LOG_ERROR << "Illegal association at "
99 << path.str;
100 continue;
101 }
102 bool containsChild = false;
103 for (const std::string &endpoint : *endpoints)
104 {
105 if (std::find(inventory.begin(), inventory.end(),
106 endpoint) != inventory.end())
107 {
108 containsChild = true;
109 break;
110 }
111 }
112 if (!containsChild)
113 {
114 continue;
115 }
116 }
117 }
118
119 if (boost::starts_with(path.str, globalInventoryPath) &&
120 boost::ends_with(path.str, "critical"))
121 {
122 health = "Critical";
123 rollup = "Critical";
124 return;
125 }
126 else if (boost::starts_with(path.str, globalInventoryPath) &&
127 boost::ends_with(path.str, "warning"))
128 {
129 health = "Warning";
130 if (rollup != "Critical")
131 {
132 rollup = "Warning";
133 }
134 }
135 else if (boost::ends_with(path.str, "critical"))
136 {
137 rollup = "Critical";
James Feist35e257a2020-06-05 13:30:51 -0700138 if (isSelf)
139 {
140 health = "Critical";
141 return;
142 }
James Feistb49ac872019-05-21 15:12:01 -0700143 }
144 else if (boost::ends_with(path.str, "warning"))
145 {
146 if (rollup != "Critical")
147 {
148 rollup = "Warning";
149 }
James Feist35e257a2020-06-05 13:30:51 -0700150
151 if (isSelf)
152 {
153 health = "Warning";
154 }
James Feistb49ac872019-05-21 15:12:01 -0700155 }
156 }
157 }
158
James Feist5bc2dc82019-10-22 14:33:16 -0700159 // this should only be called once per url, others should get updated by
160 // being added as children to the 'main' health object for the page
James Feistb49ac872019-05-21 15:12:01 -0700161 void populate()
162 {
James Feist9536a142019-11-21 09:02:32 -0800163 if (populated)
164 {
165 return;
166 }
167 populated = true;
James Feistb49ac872019-05-21 15:12:01 -0700168 getAllStatusAssociations();
169 getGlobalPath();
170 }
171
172 void getGlobalPath()
173 {
174 std::shared_ptr<HealthPopulate> self = shared_from_this();
175 crow::connections::systemBus->async_method_call(
176 [self](const boost::system::error_code ec,
177 std::vector<std::string> &resp) {
178 if (ec || resp.size() != 1)
179 {
180 // no global item, or too many
181 return;
182 }
183 self->globalInventoryPath = std::move(resp[0]);
184 },
185 "xyz.openbmc_project.ObjectMapper",
186 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -0700187 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
James Feistb49ac872019-05-21 15:12:01 -0700188 std::array<const char *, 1>{
189 "xyz.openbmc_project.Inventory.Item.Global"});
190 }
191
192 void getAllStatusAssociations()
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,
197 dbus::utility::ManagedObjectType &resp) {
198 if (ec)
199 {
200 return;
201 }
202 for (auto it = resp.begin(); it != resp.end();)
203 {
204 if (boost::ends_with(it->first.str, "critical") ||
205 boost::ends_with(it->first.str, "warning"))
206 {
207 it++;
208 continue;
209 }
210 it = resp.erase(it);
211 }
212 self->statuses = std::move(resp);
213 },
214 "xyz.openbmc_project.ObjectMapper", "/",
215 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
216 }
217
218 std::shared_ptr<AsyncResp> asyncResp;
James Feist5bc2dc82019-10-22 14:33:16 -0700219 nlohmann::json &jsonStatus;
220
221 // we store pointers to other HealthPopulate items so we can update their
222 // members and reduce dbus calls. As we hold a shared_ptr to them, they get
223 // destroyed last, and they need not call populate()
224 std::vector<std::shared_ptr<HealthPopulate>> children;
225
James Feist35e257a2020-06-05 13:30:51 -0700226 // self is used if health is for an individual items status, as this is the
227 // 'lowest most' item, the rollup will equal the health
228 std::optional<std::string> selfPath;
229
James Feistb49ac872019-05-21 15:12:01 -0700230 std::vector<std::string> inventory;
231 bool isManagersHealth = false;
232 dbus::utility::ManagedObjectType statuses;
233 std::string globalInventoryPath = "-"; // default to illegal dbus path
James Feist9536a142019-11-21 09:02:32 -0800234 bool populated = false;
James Feistb49ac872019-05-21 15:12:01 -0700235};
236} // namespace redfish