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