1 #pragma once 2 3 #include "utils/collection.hpp" 4 #include "utils/telemetry_utils.hpp" 5 6 #include <app.hpp> 7 #include <registries/privilege_registry.hpp> 8 9 #include <tuple> 10 #include <variant> 11 #include <vector> 12 13 namespace redfish 14 { 15 namespace telemetry 16 { 17 constexpr const char* triggerInterface = 18 "xyz.openbmc_project.Telemetry.Trigger"; 19 constexpr const char* triggerUri = "/redfish/v1/TelemetryService/Triggers"; 20 21 using NumericThresholdParams = 22 std::tuple<std::string, uint64_t, std::string, double>; 23 24 using DiscreteThresholdParams = 25 std::tuple<std::string, std::string, uint64_t, std::string>; 26 27 using TriggerThresholdParamsExt = 28 std::variant<std::monostate, std::vector<NumericThresholdParams>, 29 std::vector<DiscreteThresholdParams>>; 30 31 using TriggerSensorsParams = 32 std::vector<std::pair<sdbusplus::message::object_path, std::string>>; 33 34 using TriggerGetParamsVariant = 35 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt, 36 TriggerSensorsParams, std::vector<std::string>>; 37 38 inline std::optional<std::string> 39 getRedfishFromDbusAction(const std::string& dbusAction) 40 { 41 std::optional<std::string> redfishAction = std::nullopt; 42 if (dbusAction == "UpdateReport") 43 { 44 redfishAction = "RedfishMetricReport"; 45 } 46 if (dbusAction == "RedfishEvent") 47 { 48 redfishAction = "RedfishEvent"; 49 } 50 if (dbusAction == "LogToLogService") 51 { 52 redfishAction = "LogToLogService"; 53 } 54 return redfishAction; 55 } 56 57 inline std::optional<std::vector<std::string>> 58 getTriggerActions(const std::vector<std::string>& dbusActions) 59 { 60 std::vector<std::string> triggerActions; 61 for (const std::string& dbusAction : dbusActions) 62 { 63 std::optional<std::string> redfishAction = 64 getRedfishFromDbusAction(dbusAction); 65 66 if (!redfishAction) 67 { 68 return std::nullopt; 69 } 70 71 triggerActions.push_back(*redfishAction); 72 } 73 74 return std::make_optional(triggerActions); 75 } 76 77 inline std::optional<nlohmann::json> 78 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams) 79 { 80 const std::vector<DiscreteThresholdParams>* discreteParams = 81 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams); 82 83 if (discreteParams == nullptr) 84 { 85 return std::nullopt; 86 } 87 88 nlohmann::json triggers = nlohmann::json::array(); 89 for (const auto& [name, severity, dwellTime, value] : *discreteParams) 90 { 91 std::optional<std::string> duration = 92 time_utils::toDurationStringFromUint(dwellTime); 93 94 if (!duration) 95 { 96 return std::nullopt; 97 } 98 99 triggers.push_back({ 100 {"Name", name}, 101 {"Severity", severity}, 102 {"DwellTime", *duration}, 103 {"Value", value}, 104 }); 105 } 106 107 return std::make_optional(triggers); 108 } 109 110 inline std::optional<nlohmann::json> 111 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams) 112 { 113 const std::vector<NumericThresholdParams>* numericParams = 114 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams); 115 116 if (numericParams == nullptr) 117 { 118 return std::nullopt; 119 } 120 121 nlohmann::json thresholds; 122 for (const auto& [type, dwellTime, activation, reading] : *numericParams) 123 { 124 std::optional<std::string> duration = 125 time_utils::toDurationStringFromUint(dwellTime); 126 127 if (!duration) 128 { 129 return std::nullopt; 130 } 131 132 thresholds[type] = {{"Reading", reading}, 133 {"Activation", activation}, 134 {"DwellTime", *duration}}; 135 } 136 137 return std::make_optional(thresholds); 138 } 139 140 inline nlohmann::json 141 getMetricReportDefinitions(const std::vector<std::string>& reportNames) 142 { 143 nlohmann::json reports = nlohmann::json::array(); 144 for (const std::string& name : reportNames) 145 { 146 reports.push_back( 147 {{"@odata.id", 148 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService", 149 "MetricReportDefinitions", name) 150 .string()}}); 151 } 152 153 return reports; 154 } 155 156 inline std::vector<std::string> 157 getMetricProperties(const TriggerSensorsParams& sensors) 158 { 159 std::vector<std::string> metricProperties; 160 metricProperties.reserve(sensors.size()); 161 for (const auto& [_, metadata] : sensors) 162 { 163 metricProperties.emplace_back(metadata); 164 } 165 166 return metricProperties; 167 } 168 169 inline bool fillTrigger( 170 nlohmann::json& json, const std::string& id, 171 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>& 172 properties) 173 { 174 const std::string* name = nullptr; 175 const bool* discrete = nullptr; 176 const TriggerSensorsParams* sensors = nullptr; 177 const std::vector<std::string>* reports = nullptr; 178 const std::vector<std::string>* actions = nullptr; 179 const TriggerThresholdParamsExt* thresholds = nullptr; 180 181 for (const auto& [key, var] : properties) 182 { 183 if (key == "Name") 184 { 185 name = std::get_if<std::string>(&var); 186 } 187 else if (key == "Discrete") 188 { 189 discrete = std::get_if<bool>(&var); 190 } 191 else if (key == "Sensors") 192 { 193 sensors = std::get_if<TriggerSensorsParams>(&var); 194 } 195 else if (key == "ReportNames") 196 { 197 reports = std::get_if<std::vector<std::string>>(&var); 198 } 199 else if (key == "TriggerActions") 200 { 201 actions = std::get_if<std::vector<std::string>>(&var); 202 } 203 else if (key == "Thresholds") 204 { 205 thresholds = std::get_if<TriggerThresholdParamsExt>(&var); 206 } 207 } 208 209 if (name == nullptr || discrete == nullptr || sensors == nullptr || 210 reports == nullptr || actions == nullptr || thresholds == nullptr) 211 { 212 BMCWEB_LOG_ERROR 213 << "Property type mismatch or property is missing in Trigger: " 214 << id; 215 return false; 216 } 217 218 json["@odata.type"] = "#Triggers.v1_2_0.Triggers"; 219 json["@odata.id"] = crow::utility::urlFromPieces( 220 "redfish", "v1", "TelemetryService", "Triggers", id) 221 .string(); 222 json["Id"] = id; 223 json["Name"] = *name; 224 225 if (*discrete) 226 { 227 std::optional<nlohmann::json> discreteTriggers = 228 getDiscreteTriggers(*thresholds); 229 230 if (!discreteTriggers) 231 { 232 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete " 233 "triggers in Trigger: " 234 << id; 235 return false; 236 } 237 238 json["DiscreteTriggers"] = *discreteTriggers; 239 json["DiscreteTriggerCondition"] = 240 discreteTriggers->empty() ? "Changed" : "Specified"; 241 json["MetricType"] = "Discrete"; 242 } 243 else 244 { 245 std::optional<nlohmann::json> numericThresholds = 246 getNumericThresholds(*thresholds); 247 248 if (!numericThresholds) 249 { 250 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric " 251 "thresholds in Trigger: " 252 << id; 253 return false; 254 } 255 256 json["NumericThresholds"] = *numericThresholds; 257 json["MetricType"] = "Numeric"; 258 } 259 260 std::optional<std::vector<std::string>> triggerActions = 261 getTriggerActions(*actions); 262 263 if (!triggerActions) 264 { 265 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: " 266 << id; 267 return false; 268 } 269 270 json["TriggerActions"] = *triggerActions; 271 json["MetricProperties"] = getMetricProperties(*sensors); 272 json["Links"]["MetricReportDefinitions"] = 273 getMetricReportDefinitions(*reports); 274 275 return true; 276 } 277 278 } // namespace telemetry 279 280 inline void requestRoutesTriggerCollection(App& app) 281 { 282 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/") 283 .privileges(redfish::privileges::getTriggersCollection) 284 .methods(boost::beast::http::verb::get)( 285 [](const crow::Request&, 286 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 287 asyncResp->res.jsonValue["@odata.type"] = 288 "#TriggersCollection.TriggersCollection"; 289 asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri; 290 asyncResp->res.jsonValue["Name"] = "Triggers Collection"; 291 const std::vector<const char*> interfaces{ 292 telemetry::triggerInterface}; 293 collection_util::getCollectionMembers( 294 asyncResp, telemetry::triggerUri, interfaces, 295 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService"); 296 }); 297 } 298 299 inline void requestRoutesTrigger(App& app) 300 { 301 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/") 302 .privileges(redfish::privileges::getTriggers) 303 .methods(boost::beast::http::verb::get)( 304 [](const crow::Request&, 305 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 306 const std::string& id) { 307 crow::connections::systemBus->async_method_call( 308 [asyncResp, 309 id](const boost::system::error_code ec, 310 const std::vector<std::pair< 311 std::string, telemetry::TriggerGetParamsVariant>>& 312 ret) { 313 if (ec.value() == EBADR || 314 ec == boost::system::errc::host_unreachable) 315 { 316 messages::resourceNotFound(asyncResp->res, 317 "Triggers", id); 318 return; 319 } 320 if (ec) 321 { 322 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 323 messages::internalError(asyncResp->res); 324 return; 325 } 326 327 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, 328 id, ret)) 329 { 330 messages::internalError(asyncResp->res); 331 } 332 }, 333 telemetry::service, telemetry::getDbusTriggerPath(id), 334 "org.freedesktop.DBus.Properties", "GetAll", 335 telemetry::triggerInterface); 336 }); 337 338 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/") 339 .privileges(redfish::privileges::deleteTriggers) 340 .methods(boost::beast::http::verb::delete_)( 341 [](const crow::Request&, 342 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 343 const std::string& id) { 344 const std::string triggerPath = 345 telemetry::getDbusTriggerPath(id); 346 347 crow::connections::systemBus->async_method_call( 348 [asyncResp, id](const boost::system::error_code ec) { 349 if (ec.value() == EBADR) 350 { 351 messages::resourceNotFound(asyncResp->res, 352 "Triggers", id); 353 return; 354 } 355 356 if (ec) 357 { 358 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 359 messages::internalError(asyncResp->res); 360 return; 361 } 362 363 asyncResp->res.result( 364 boost::beast::http::status::no_content); 365 }, 366 telemetry::service, triggerPath, 367 "xyz.openbmc_project.Object.Delete", "Delete"); 368 }); 369 } 370 371 } // namespace redfish 372