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", metricReportDefinitionUri + std::string("/") + name}, 148 }); 149 } 150 151 return reports; 152 } 153 154 inline std::vector<std::string> 155 getMetricProperties(const TriggerSensorsParams& sensors) 156 { 157 std::vector<std::string> metricProperties; 158 metricProperties.reserve(sensors.size()); 159 for (const auto& [_, metadata] : sensors) 160 { 161 metricProperties.emplace_back(metadata); 162 } 163 164 return metricProperties; 165 } 166 167 inline bool fillTrigger( 168 nlohmann::json& json, const std::string& id, 169 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>& 170 properties) 171 { 172 const std::string* name = nullptr; 173 const bool* discrete = nullptr; 174 const TriggerSensorsParams* sensors = nullptr; 175 const std::vector<std::string>* reports = nullptr; 176 const std::vector<std::string>* actions = nullptr; 177 const TriggerThresholdParamsExt* thresholds = nullptr; 178 179 for (const auto& [key, var] : properties) 180 { 181 if (key == "Name") 182 { 183 name = std::get_if<std::string>(&var); 184 } 185 else if (key == "Discrete") 186 { 187 discrete = std::get_if<bool>(&var); 188 } 189 else if (key == "Sensors") 190 { 191 sensors = std::get_if<TriggerSensorsParams>(&var); 192 } 193 else if (key == "ReportNames") 194 { 195 reports = std::get_if<std::vector<std::string>>(&var); 196 } 197 else if (key == "TriggerActions") 198 { 199 actions = std::get_if<std::vector<std::string>>(&var); 200 } 201 else if (key == "Thresholds") 202 { 203 thresholds = std::get_if<TriggerThresholdParamsExt>(&var); 204 } 205 } 206 207 if (name == nullptr || discrete == nullptr || sensors == nullptr || 208 reports == nullptr || actions == nullptr || thresholds == nullptr) 209 { 210 BMCWEB_LOG_ERROR 211 << "Property type mismatch or property is missing in Trigger: " 212 << id; 213 return false; 214 } 215 216 json["@odata.type"] = "#Triggers.v1_2_0.Triggers"; 217 json["@odata.id"] = triggerUri + std::string("/") + id; 218 json["Id"] = id; 219 json["Name"] = *name; 220 221 if (*discrete) 222 { 223 std::optional<nlohmann::json> discreteTriggers = 224 getDiscreteTriggers(*thresholds); 225 226 if (!discreteTriggers) 227 { 228 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete " 229 "triggers in Trigger: " 230 << id; 231 return false; 232 } 233 234 json["DiscreteTriggers"] = *discreteTriggers; 235 json["DiscreteTriggerCondition"] = 236 discreteTriggers->empty() ? "Changed" : "Specified"; 237 json["MetricType"] = "Discrete"; 238 } 239 else 240 { 241 std::optional<nlohmann::json> numericThresholds = 242 getNumericThresholds(*thresholds); 243 244 if (!numericThresholds) 245 { 246 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric " 247 "thresholds in Trigger: " 248 << id; 249 return false; 250 } 251 252 json["NumericThresholds"] = *numericThresholds; 253 json["MetricType"] = "Numeric"; 254 } 255 256 std::optional<std::vector<std::string>> triggerActions = 257 getTriggerActions(*actions); 258 259 if (!triggerActions) 260 { 261 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: " 262 << id; 263 return false; 264 } 265 266 json["TriggerActions"] = *triggerActions; 267 json["MetricProperties"] = getMetricProperties(*sensors); 268 json["Links"]["MetricReportDefinitions"] = 269 getMetricReportDefinitions(*reports); 270 271 return true; 272 } 273 274 } // namespace telemetry 275 276 inline void requestRoutesTriggerCollection(App& app) 277 { 278 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/") 279 .privileges(redfish::privileges::getTriggersCollection) 280 .methods(boost::beast::http::verb::get)( 281 [](const crow::Request&, 282 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 283 asyncResp->res.jsonValue["@odata.type"] = 284 "#TriggersCollection.TriggersCollection"; 285 asyncResp->res.jsonValue["@odata.id"] = 286 "/redfish/v1/TelemetryService/Triggers"; 287 asyncResp->res.jsonValue["Name"] = "Triggers Collection"; 288 const std::vector<const char*> interfaces{ 289 telemetry::triggerInterface}; 290 collection_util::getCollectionMembers( 291 asyncResp, telemetry::triggerUri, interfaces, 292 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService"); 293 }); 294 } 295 296 inline void requestRoutesTrigger(App& app) 297 { 298 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/") 299 .privileges(redfish::privileges::getTriggers) 300 .methods(boost::beast::http::verb::get)( 301 [](const crow::Request&, 302 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 303 const std::string& id) { 304 crow::connections::systemBus->async_method_call( 305 [asyncResp, 306 id](const boost::system::error_code ec, 307 const std::vector<std::pair< 308 std::string, telemetry::TriggerGetParamsVariant>>& 309 ret) { 310 if (ec.value() == EBADR || 311 ec == boost::system::errc::host_unreachable) 312 { 313 messages::resourceNotFound(asyncResp->res, 314 "Triggers", id); 315 return; 316 } 317 if (ec) 318 { 319 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 320 messages::internalError(asyncResp->res); 321 return; 322 } 323 324 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, 325 id, ret)) 326 { 327 messages::internalError(asyncResp->res); 328 } 329 }, 330 telemetry::service, telemetry::getDbusTriggerPath(id), 331 "org.freedesktop.DBus.Properties", "GetAll", 332 telemetry::triggerInterface); 333 }); 334 335 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/") 336 .privileges(redfish::privileges::deleteTriggers) 337 .methods(boost::beast::http::verb::delete_)( 338 [](const crow::Request&, 339 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 340 const std::string& id) { 341 const std::string triggerPath = 342 telemetry::getDbusTriggerPath(id); 343 344 crow::connections::systemBus->async_method_call( 345 [asyncResp, id](const boost::system::error_code ec) { 346 if (ec.value() == EBADR) 347 { 348 messages::resourceNotFound(asyncResp->res, 349 "Triggers", id); 350 return; 351 } 352 353 if (ec) 354 { 355 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 356 messages::internalError(asyncResp->res); 357 return; 358 } 359 360 asyncResp->res.result( 361 boost::beast::http::status::no_content); 362 }, 363 telemetry::service, triggerPath, 364 "xyz.openbmc_project.Object.Delete", "Delete"); 365 }); 366 } 367 368 } // namespace redfish 369