140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation 446229577SJames Feist #pragma once 546229577SJames Feist 63ccb3adbSEd Tanous #include "app.hpp" 7d7857201SEd Tanous #include "async_resp.hpp" 8d7857201SEd Tanous #include "dbus_singleton.hpp" 9d7857201SEd Tanous #include "error_messages.hpp" 103ccb3adbSEd Tanous #include "event_service_manager.hpp" 11539d8c6bSEd Tanous #include "generated/enums/resource.hpp" 12539d8c6bSEd Tanous #include "generated/enums/task_service.hpp" 131aa0c2b8SEd Tanous #include "http/parsing.hpp" 14d7857201SEd Tanous #include "http_request.hpp" 15d7857201SEd Tanous #include "http_response.hpp" 16d7857201SEd Tanous #include "logging.hpp" 173ccb3adbSEd Tanous #include "query.hpp" 183ccb3adbSEd Tanous #include "registries/privilege_registry.hpp" 193ccb3adbSEd Tanous #include "task_messages.hpp" 20*08fad5d9SCorey Ethington #include "utils/etag_utils.hpp" 21d7857201SEd Tanous #include "utils/time_utils.hpp" 223ccb3adbSEd Tanous 23d7857201SEd Tanous #include <boost/asio/error.hpp> 24d43cd0caSEd Tanous #include <boost/asio/post.hpp> 25d43cd0caSEd Tanous #include <boost/asio/steady_timer.hpp> 26d7857201SEd Tanous #include <boost/beast/http/field.hpp> 27d7857201SEd Tanous #include <boost/beast/http/status.hpp> 28d7857201SEd Tanous #include <boost/beast/http/verb.hpp> 29ef4c65b7SEd Tanous #include <boost/url/format.hpp> 30d7857201SEd Tanous #include <boost/url/url.hpp> 31d7857201SEd Tanous #include <nlohmann/json.hpp> 32d7857201SEd Tanous #include <sdbusplus/bus.hpp> 333ccb3adbSEd Tanous #include <sdbusplus/bus/match.hpp> 34d7857201SEd Tanous #include <sdbusplus/message.hpp> 351214b7e7SGunnar Mills 36d7857201SEd Tanous #include <algorithm> 37d7857201SEd Tanous #include <array> 381214b7e7SGunnar Mills #include <chrono> 39d7857201SEd Tanous #include <cstddef> 40d7857201SEd Tanous #include <ctime> 41d7857201SEd Tanous #include <deque> 42d7857201SEd Tanous #include <functional> 433ccb3adbSEd Tanous #include <memory> 44d7857201SEd Tanous #include <optional> 453544d2a7SEd Tanous #include <ranges> 46d7857201SEd Tanous #include <string> 47d7857201SEd Tanous #include <string_view> 48d7857201SEd Tanous #include <utility> 4946229577SJames Feist 5046229577SJames Feist namespace redfish 5146229577SJames Feist { 5246229577SJames Feist 5346229577SJames Feist namespace task 5446229577SJames Feist { 5546229577SJames Feist constexpr size_t maxTaskCount = 100; // arbitrary limit 5646229577SJames Feist 57cf9e417dSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) 5846229577SJames Feist static std::deque<std::shared_ptr<struct TaskData>> tasks; 5946229577SJames Feist 6032898ceaSJames Feist constexpr bool completed = true; 6132898ceaSJames Feist 62fe306728SJames Feist struct Payload 63fe306728SJames Feist { 644e23a444SEd Tanous explicit Payload(const crow::Request& req) : 6539662a3bSEd Tanous targetUri(req.url().encoded_path()), httpOperation(req.methodString()), 661aa0c2b8SEd Tanous httpHeaders(nlohmann::json::array()) 67fe306728SJames Feist { 68fe306728SJames Feist using field_ns = boost::beast::http::field; 69fe306728SJames Feist constexpr const std::array<boost::beast::http::field, 7> 70fe306728SJames Feist headerWhitelist = {field_ns::accept, field_ns::accept_encoding, 71fe306728SJames Feist field_ns::user_agent, field_ns::host, 72fe306728SJames Feist field_ns::connection, field_ns::content_length, 73fe306728SJames Feist field_ns::upgrade}; 74fe306728SJames Feist 751aa0c2b8SEd Tanous JsonParseResult ret = parseRequestAsJson(req, jsonBody); 761aa0c2b8SEd Tanous if (ret != JsonParseResult::Success) 77fe306728SJames Feist { 781aa0c2b8SEd Tanous return; 79fe306728SJames Feist } 80fe306728SJames Feist 8198fe740bSEd Tanous for (const auto& field : req.fields()) 82fe306728SJames Feist { 833544d2a7SEd Tanous if (std::ranges::find(headerWhitelist, field.name()) == 843544d2a7SEd Tanous headerWhitelist.end()) 85fe306728SJames Feist { 86fe306728SJames Feist continue; 87fe306728SJames Feist } 88fe306728SJames Feist std::string header; 89bd79bce8SPatrick Williams header.reserve( 90bd79bce8SPatrick Williams field.name_string().size() + 2 + field.value().size()); 91fe306728SJames Feist header += field.name_string(); 92fe306728SJames Feist header += ": "; 93fe306728SJames Feist header += field.value(); 94fe306728SJames Feist httpHeaders.emplace_back(std::move(header)); 95fe306728SJames Feist } 96fe306728SJames Feist } 97fe306728SJames Feist Payload() = delete; 98fe306728SJames Feist 99fe306728SJames Feist std::string targetUri; 100fe306728SJames Feist std::string httpOperation; 101fe306728SJames Feist nlohmann::json httpHeaders; 102fe306728SJames Feist nlohmann::json jsonBody; 103fe306728SJames Feist }; 104fe306728SJames Feist 10546229577SJames Feist struct TaskData : std::enable_shared_from_this<TaskData> 10646229577SJames Feist { 10746229577SJames Feist private: 10859d494eeSPatrick Williams TaskData( 10959d494eeSPatrick Williams std::function<bool(boost::system::error_code, sdbusplus::message_t&, 11046229577SJames Feist const std::shared_ptr<TaskData>&)>&& handler, 11123a21a1cSEd Tanous const std::string& matchIn, size_t idx) : 112bd79bce8SPatrick Williams callback(std::move(handler)), matchStr(matchIn), index(idx), 11346229577SJames Feist startTime(std::chrono::system_clock::to_time_t( 11446229577SJames Feist std::chrono::system_clock::now())), 11546229577SJames Feist status("OK"), state("Running"), messages(nlohmann::json::array()), 11646229577SJames Feist timer(crow::connections::systemBus->get_io_context()) 11746229577SJames Feist 1181214b7e7SGunnar Mills {} 11946229577SJames Feist 12046229577SJames Feist public: 121d609fd6eSEd Tanous TaskData() = delete; 122d609fd6eSEd Tanous 12346229577SJames Feist static std::shared_ptr<TaskData>& createTask( 12459d494eeSPatrick Williams std::function<bool(boost::system::error_code, sdbusplus::message_t&, 12546229577SJames Feist const std::shared_ptr<TaskData>&)>&& handler, 12646229577SJames Feist const std::string& match) 12746229577SJames Feist { 12846229577SJames Feist static size_t lastTask = 0; 12946229577SJames Feist struct MakeSharedHelper : public TaskData 13046229577SJames Feist { 13146229577SJames Feist MakeSharedHelper( 1321214b7e7SGunnar Mills std::function<bool(boost::system::error_code, 13359d494eeSPatrick Williams sdbusplus::message_t&, 13446229577SJames Feist const std::shared_ptr<TaskData>&)>&& handler, 13523a21a1cSEd Tanous const std::string& match2, size_t idx) : 13623a21a1cSEd Tanous TaskData(std::move(handler), match2, idx) 1371214b7e7SGunnar Mills {} 13846229577SJames Feist }; 13946229577SJames Feist 14046229577SJames Feist if (tasks.size() >= maxTaskCount) 14146229577SJames Feist { 1429f03894eSRohit PAI const auto last = getTaskToRemove(); 14346229577SJames Feist 14446229577SJames Feist // destroy all references 1459f03894eSRohit PAI (*last)->timer.cancel(); 1469f03894eSRohit PAI (*last)->match.reset(); 1479f03894eSRohit PAI tasks.erase(last); 14846229577SJames Feist } 14946229577SJames Feist 15046229577SJames Feist return tasks.emplace_back(std::make_shared<MakeSharedHelper>( 15146229577SJames Feist std::move(handler), match, lastTask++)); 15246229577SJames Feist } 15346229577SJames Feist 1549f03894eSRohit PAI /** 1559f03894eSRohit PAI * @brief Get the first completed/aborted task or oldest running task to 1569f03894eSRohit PAI * remove 1579f03894eSRohit PAI */ 1589f03894eSRohit PAI static std::deque<std::shared_ptr<TaskData>>::iterator getTaskToRemove() 1599f03894eSRohit PAI { 1609f03894eSRohit PAI static constexpr std::array<std::string_view, 5> activeStates = { 1619f03894eSRohit PAI "Running", "Pending", "Starting", "Suspended", "Interrupted"}; 1629f03894eSRohit PAI 1639f03894eSRohit PAI auto it = 1649f03894eSRohit PAI std::find_if(tasks.begin(), tasks.end(), [](const auto& task) { 1659f03894eSRohit PAI return std::ranges::find(activeStates, task->state) == 1669f03894eSRohit PAI activeStates.end(); 1679f03894eSRohit PAI }); 1689f03894eSRohit PAI 1699f03894eSRohit PAI return (it != tasks.end()) ? it : tasks.begin(); 1709f03894eSRohit PAI } 1719f03894eSRohit PAI 17246229577SJames Feist void populateResp(crow::Response& res, size_t retryAfterSeconds = 30) 17346229577SJames Feist { 17446229577SJames Feist if (!endTime) 17546229577SJames Feist { 17646229577SJames Feist res.result(boost::beast::http::status::accepted); 17746229577SJames Feist std::string strIdx = std::to_string(index); 178fdbce79bSEd Tanous boost::urls::url uri = 179fdbce79bSEd Tanous boost::urls::format("/redfish/v1/TaskService/Tasks/{}", strIdx); 1801476687dSEd Tanous 1811476687dSEd Tanous res.jsonValue["@odata.id"] = uri; 1821476687dSEd Tanous res.jsonValue["@odata.type"] = "#Task.v1_4_3.Task"; 1831476687dSEd Tanous res.jsonValue["Id"] = strIdx; 1841476687dSEd Tanous res.jsonValue["TaskState"] = state; 1851476687dSEd Tanous res.jsonValue["TaskStatus"] = status; 1861476687dSEd Tanous 187fdbce79bSEd Tanous boost::urls::url taskMonitor = boost::urls::format( 188fdbce79bSEd Tanous "/redfish/v1/TaskService/TaskMonitors/{}", strIdx); 189fdbce79bSEd Tanous 19046229577SJames Feist res.addHeader(boost::beast::http::field::location, 191fdbce79bSEd Tanous taskMonitor.buffer()); 19246229577SJames Feist res.addHeader(boost::beast::http::field::retry_after, 19346229577SJames Feist std::to_string(retryAfterSeconds)); 19429e2bdd7SChinmay Shripad Hegde res.jsonValue["Name"] = "Task " + strIdx; 19529e2bdd7SChinmay Shripad Hegde res.jsonValue["StartTime"] = 19629e2bdd7SChinmay Shripad Hegde redfish::time_utils::getDateTimeStdtime(startTime); 19729e2bdd7SChinmay Shripad Hegde res.jsonValue["Messages"] = messages; 19829e2bdd7SChinmay Shripad Hegde res.jsonValue["TaskMonitor"] = taskMonitor; 19929e2bdd7SChinmay Shripad Hegde res.jsonValue["HidePayload"] = !payload; 20029e2bdd7SChinmay Shripad Hegde if (payload) 20129e2bdd7SChinmay Shripad Hegde { 20229e2bdd7SChinmay Shripad Hegde const task::Payload& p = *payload; 20329e2bdd7SChinmay Shripad Hegde nlohmann::json::object_t payloadObj; 20429e2bdd7SChinmay Shripad Hegde payloadObj["TargetUri"] = p.targetUri; 20529e2bdd7SChinmay Shripad Hegde payloadObj["HttpOperation"] = p.httpOperation; 20629e2bdd7SChinmay Shripad Hegde payloadObj["HttpHeaders"] = p.httpHeaders; 20729e2bdd7SChinmay Shripad Hegde if (p.jsonBody.is_object()) 20829e2bdd7SChinmay Shripad Hegde { 20929e2bdd7SChinmay Shripad Hegde payloadObj["JsonBody"] = p.jsonBody.dump( 21029e2bdd7SChinmay Shripad Hegde 2, ' ', true, nlohmann::json::error_handler_t::replace); 21129e2bdd7SChinmay Shripad Hegde } 21229e2bdd7SChinmay Shripad Hegde res.jsonValue["Payload"] = std::move(payloadObj); 21329e2bdd7SChinmay Shripad Hegde } 21429e2bdd7SChinmay Shripad Hegde res.jsonValue["PercentComplete"] = percentComplete; 21546229577SJames Feist } 21646229577SJames Feist else if (!gave204) 21746229577SJames Feist { 21846229577SJames Feist res.result(boost::beast::http::status::no_content); 21946229577SJames Feist gave204 = true; 22046229577SJames Feist } 22146229577SJames Feist } 22246229577SJames Feist 223d609fd6eSEd Tanous void finishTask() 22446229577SJames Feist { 22546229577SJames Feist endTime = std::chrono::system_clock::to_time_t( 22646229577SJames Feist std::chrono::system_clock::now()); 22746229577SJames Feist } 22846229577SJames Feist 229fd9ab9e1SJames Feist void extendTimer(const std::chrono::seconds& timeout) 23046229577SJames Feist { 23146229577SJames Feist timer.expires_after(timeout); 23246229577SJames Feist timer.async_wait( 23346229577SJames Feist [self = shared_from_this()](boost::system::error_code ec) { 23446229577SJames Feist if (ec == boost::asio::error::operation_aborted) 23546229577SJames Feist { 2364e0453b1SGunnar Mills return; // completed successfully 23746229577SJames Feist } 23846229577SJames Feist if (!ec) 23946229577SJames Feist { 24046229577SJames Feist // change ec to error as timer expired 24146229577SJames Feist ec = boost::asio::error::operation_aborted; 24246229577SJames Feist } 24346229577SJames Feist self->match.reset(); 24459d494eeSPatrick Williams sdbusplus::message_t msg; 24546229577SJames Feist self->finishTask(); 24646229577SJames Feist self->state = "Cancelled"; 24746229577SJames Feist self->status = "Warning"; 248e5d5006bSJames Feist self->messages.emplace_back( 249e5d5006bSJames Feist messages::taskAborted(std::to_string(self->index))); 250e7686576SSunitha Harish // Send event :TaskAborted 251daadfb2eSEd Tanous sendTaskEvent(self->state, self->index); 25246229577SJames Feist self->callback(ec, msg, self); 25346229577SJames Feist }); 254fd9ab9e1SJames Feist } 255fd9ab9e1SJames Feist 25626ccae32SEd Tanous static void sendTaskEvent(std::string_view state, size_t index) 257e7686576SSunitha Harish { 258e7686576SSunitha Harish // TaskState enums which should send out an event are: 259e7686576SSunitha Harish // "Starting" = taskResumed 260e7686576SSunitha Harish // "Running" = taskStarted 261e7686576SSunitha Harish // "Suspended" = taskPaused 262e7686576SSunitha Harish // "Interrupted" = taskPaused 263e7686576SSunitha Harish // "Pending" = taskPaused 264e7686576SSunitha Harish // "Stopping" = taskAborted 265e7686576SSunitha Harish // "Completed" = taskCompletedOK 266e7686576SSunitha Harish // "Killed" = taskRemoved 267e7686576SSunitha Harish // "Exception" = taskCompletedWarning 268e7686576SSunitha Harish // "Cancelled" = taskCancelled 26982b286fbSEd Tanous nlohmann::json::object_t event; 270f8fe2211SEd Tanous std::string indexStr = std::to_string(index); 271e7686576SSunitha Harish if (state == "Starting") 272e7686576SSunitha Harish { 273f8fe2211SEd Tanous event = redfish::messages::taskResumed(indexStr); 274e7686576SSunitha Harish } 275e7686576SSunitha Harish else if (state == "Running") 276e7686576SSunitha Harish { 277f8fe2211SEd Tanous event = redfish::messages::taskStarted(indexStr); 278e7686576SSunitha Harish } 279e7686576SSunitha Harish else if ((state == "Suspended") || (state == "Interrupted") || 280e7686576SSunitha Harish (state == "Pending")) 281e7686576SSunitha Harish { 282f8fe2211SEd Tanous event = redfish::messages::taskPaused(indexStr); 283e7686576SSunitha Harish } 284e7686576SSunitha Harish else if (state == "Stopping") 285e7686576SSunitha Harish { 286f8fe2211SEd Tanous event = redfish::messages::taskAborted(indexStr); 287e7686576SSunitha Harish } 288e7686576SSunitha Harish else if (state == "Completed") 289e7686576SSunitha Harish { 290f8fe2211SEd Tanous event = redfish::messages::taskCompletedOK(indexStr); 291e7686576SSunitha Harish } 292e7686576SSunitha Harish else if (state == "Killed") 293e7686576SSunitha Harish { 294f8fe2211SEd Tanous event = redfish::messages::taskRemoved(indexStr); 295e7686576SSunitha Harish } 296e7686576SSunitha Harish else if (state == "Exception") 297e7686576SSunitha Harish { 298f8fe2211SEd Tanous event = redfish::messages::taskCompletedWarning(indexStr); 299e7686576SSunitha Harish } 300e7686576SSunitha Harish else if (state == "Cancelled") 301e7686576SSunitha Harish { 302f8fe2211SEd Tanous event = redfish::messages::taskCancelled(indexStr); 303e7686576SSunitha Harish } 304e7686576SSunitha Harish else 305e7686576SSunitha Harish { 30662598e31SEd Tanous BMCWEB_LOG_INFO("sendTaskEvent: No events to send"); 307f8fe2211SEd Tanous return; 308e7686576SSunitha Harish } 309f8fe2211SEd Tanous boost::urls::url origin = 310f8fe2211SEd Tanous boost::urls::format("/redfish/v1/TaskService/Tasks/{}", index); 311f8fe2211SEd Tanous EventServiceManager::getInstance().sendEvent(event, origin.buffer(), 312f8fe2211SEd Tanous "Task"); 313e7686576SSunitha Harish } 314e7686576SSunitha Harish 315fd9ab9e1SJames Feist void startTimer(const std::chrono::seconds& timeout) 316fd9ab9e1SJames Feist { 317fd9ab9e1SJames Feist if (match) 318fd9ab9e1SJames Feist { 319fd9ab9e1SJames Feist return; 320fd9ab9e1SJames Feist } 32159d494eeSPatrick Williams match = std::make_unique<sdbusplus::bus::match_t>( 32259d494eeSPatrick Williams static_cast<sdbusplus::bus_t&>(*crow::connections::systemBus), 323fd9ab9e1SJames Feist matchStr, 32459d494eeSPatrick Williams [self = shared_from_this()](sdbusplus::message_t& message) { 325fd9ab9e1SJames Feist boost::system::error_code ec; 326fd9ab9e1SJames Feist 327fd9ab9e1SJames Feist // callback to return True if callback is done, callback needs 328fd9ab9e1SJames Feist // to update status itself if needed 329fd9ab9e1SJames Feist if (self->callback(ec, message, self) == task::completed) 330fd9ab9e1SJames Feist { 331fd9ab9e1SJames Feist self->timer.cancel(); 332fd9ab9e1SJames Feist self->finishTask(); 333fd9ab9e1SJames Feist 334e7686576SSunitha Harish // Send event 335daadfb2eSEd Tanous sendTaskEvent(self->state, self->index); 336e7686576SSunitha Harish 337fd9ab9e1SJames Feist // reset the match after the callback was successful 338fd9ab9e1SJames Feist boost::asio::post( 339fd9ab9e1SJames Feist crow::connections::systemBus->get_io_context(), 340fd9ab9e1SJames Feist [self] { self->match.reset(); }); 341fd9ab9e1SJames Feist return; 342fd9ab9e1SJames Feist } 343fd9ab9e1SJames Feist }); 344fd9ab9e1SJames Feist 345fd9ab9e1SJames Feist extendTimer(timeout); 346e5d5006bSJames Feist messages.emplace_back(messages::taskStarted(std::to_string(index))); 347e7686576SSunitha Harish // Send event : TaskStarted 348e7686576SSunitha Harish sendTaskEvent(state, index); 34946229577SJames Feist } 35046229577SJames Feist 35159d494eeSPatrick Williams std::function<bool(boost::system::error_code, sdbusplus::message_t&, 35246229577SJames Feist const std::shared_ptr<TaskData>&)> 35346229577SJames Feist callback; 35446229577SJames Feist std::string matchStr; 35546229577SJames Feist size_t index; 35646229577SJames Feist time_t startTime; 35746229577SJames Feist std::string status; 35846229577SJames Feist std::string state; 35946229577SJames Feist nlohmann::json messages; 36046229577SJames Feist boost::asio::steady_timer timer; 36159d494eeSPatrick Williams std::unique_ptr<sdbusplus::bus::match_t> match; 36246229577SJames Feist std::optional<time_t> endTime; 363fe306728SJames Feist std::optional<Payload> payload; 36446229577SJames Feist bool gave204 = false; 3656868ff50SGeorge Liu int percentComplete = 0; 36646229577SJames Feist }; 36746229577SJames Feist 36846229577SJames Feist } // namespace task 36946229577SJames Feist 3707e860f15SJohn Edward Broadbent inline void requestRoutesTaskMonitor(App& app) 37146229577SJames Feist { 372fdbce79bSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/TaskService/TaskMonitors/<str>/") 373ed398213SEd Tanous .privileges(redfish::privileges::getTask) 3747e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::get)( 37545ca1b86SEd Tanous [&app](const crow::Request& req, 3767e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3777e860f15SJohn Edward Broadbent const std::string& strParam) { 3783ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 37945ca1b86SEd Tanous { 38045ca1b86SEd Tanous return; 38145ca1b86SEd Tanous } 3823544d2a7SEd Tanous auto find = std::ranges::find_if( 3833544d2a7SEd Tanous task::tasks, 38446229577SJames Feist [&strParam](const std::shared_ptr<task::TaskData>& task) { 38546229577SJames Feist if (!task) 38646229577SJames Feist { 38746229577SJames Feist return false; 38846229577SJames Feist } 38946229577SJames Feist 3907e860f15SJohn Edward Broadbent // we compare against the string version as on failure 3917e860f15SJohn Edward Broadbent // strtoul returns 0 39246229577SJames Feist return std::to_string(task->index) == strParam; 39346229577SJames Feist }); 39446229577SJames Feist 39546229577SJames Feist if (find == task::tasks.end()) 39646229577SJames Feist { 397bd79bce8SPatrick Williams messages::resourceNotFound(asyncResp->res, "Task", 398bd79bce8SPatrick Williams strParam); 39946229577SJames Feist return; 40046229577SJames Feist } 40146229577SJames Feist std::shared_ptr<task::TaskData>& ptr = *find; 40246229577SJames Feist // monitor expires after 204 40346229577SJames Feist if (ptr->gave204) 40446229577SJames Feist { 405bd79bce8SPatrick Williams messages::resourceNotFound(asyncResp->res, "Task", 406bd79bce8SPatrick Williams strParam); 40746229577SJames Feist return; 40846229577SJames Feist } 40946229577SJames Feist ptr->populateResp(asyncResp->res); 4107e860f15SJohn Edward Broadbent }); 41146229577SJames Feist } 41246229577SJames Feist 4137e860f15SJohn Edward Broadbent inline void requestRoutesTask(App& app) 41446229577SJames Feist { 4157e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/TaskService/Tasks/<str>/") 416ed398213SEd Tanous .privileges(redfish::privileges::getTask) 4177e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::get)( 41845ca1b86SEd Tanous [&app](const crow::Request& req, 4197e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 4207e860f15SJohn Edward Broadbent const std::string& strParam) { 4213ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 42245ca1b86SEd Tanous { 42345ca1b86SEd Tanous return; 42445ca1b86SEd Tanous } 4253544d2a7SEd Tanous auto find = std::ranges::find_if( 4263544d2a7SEd Tanous task::tasks, 42746229577SJames Feist [&strParam](const std::shared_ptr<task::TaskData>& task) { 42846229577SJames Feist if (!task) 42946229577SJames Feist { 43046229577SJames Feist return false; 43146229577SJames Feist } 43246229577SJames Feist 4337e860f15SJohn Edward Broadbent // we compare against the string version as on failure 4347e860f15SJohn Edward Broadbent // strtoul returns 0 43546229577SJames Feist return std::to_string(task->index) == strParam; 43646229577SJames Feist }); 43746229577SJames Feist 43846229577SJames Feist if (find == task::tasks.end()) 43946229577SJames Feist { 440bd79bce8SPatrick Williams messages::resourceNotFound(asyncResp->res, "Task", 441bd79bce8SPatrick Williams strParam); 44246229577SJames Feist return; 44346229577SJames Feist } 44446229577SJames Feist 44502cad96eSEd Tanous const std::shared_ptr<task::TaskData>& ptr = *find; 44646229577SJames Feist 44746229577SJames Feist asyncResp->res.jsonValue["@odata.type"] = "#Task.v1_4_3.Task"; 44846229577SJames Feist asyncResp->res.jsonValue["Id"] = strParam; 44946229577SJames Feist asyncResp->res.jsonValue["Name"] = "Task " + strParam; 45046229577SJames Feist asyncResp->res.jsonValue["TaskState"] = ptr->state; 45146229577SJames Feist asyncResp->res.jsonValue["StartTime"] = 4522b82937eSEd Tanous redfish::time_utils::getDateTimeStdtime(ptr->startTime); 45346229577SJames Feist if (ptr->endTime) 45446229577SJames Feist { 45546229577SJames Feist asyncResp->res.jsonValue["EndTime"] = 456bd79bce8SPatrick Williams redfish::time_utils::getDateTimeStdtime( 457bd79bce8SPatrick Williams *(ptr->endTime)); 45846229577SJames Feist } 45946229577SJames Feist asyncResp->res.jsonValue["TaskStatus"] = ptr->status; 46046229577SJames Feist asyncResp->res.jsonValue["Messages"] = ptr->messages; 461bd79bce8SPatrick Williams asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 462bd79bce8SPatrick Williams "/redfish/v1/TaskService/Tasks/{}", strParam); 46346229577SJames Feist if (!ptr->gave204) 46446229577SJames Feist { 465bd79bce8SPatrick Williams asyncResp->res.jsonValue["TaskMonitor"] = 466bd79bce8SPatrick Williams boost::urls::format( 467bd79bce8SPatrick Williams "/redfish/v1/TaskService/TaskMonitors/{}", 468bd79bce8SPatrick Williams strParam); 46946229577SJames Feist } 4705db7dfd6SArun Thomas Baby 4715db7dfd6SArun Thomas Baby asyncResp->res.jsonValue["HidePayload"] = !ptr->payload; 4725db7dfd6SArun Thomas Baby 473fe306728SJames Feist if (ptr->payload) 474fe306728SJames Feist { 4755fb91ba4SEd Tanous const task::Payload& p = *(ptr->payload); 476bd79bce8SPatrick Williams asyncResp->res.jsonValue["Payload"]["TargetUri"] = 477bd79bce8SPatrick Williams p.targetUri; 4781476687dSEd Tanous asyncResp->res.jsonValue["Payload"]["HttpOperation"] = 4791476687dSEd Tanous p.httpOperation; 480bd79bce8SPatrick Williams asyncResp->res.jsonValue["Payload"]["HttpHeaders"] = 481bd79bce8SPatrick Williams p.httpHeaders; 482bd79bce8SPatrick Williams asyncResp->res.jsonValue["Payload"]["JsonBody"] = 483bd79bce8SPatrick Williams p.jsonBody.dump( 484bd79bce8SPatrick Williams -1, ' ', true, 485bd79bce8SPatrick Williams nlohmann::json::error_handler_t::replace); 486fe306728SJames Feist } 487bd79bce8SPatrick Williams asyncResp->res.jsonValue["PercentComplete"] = 488bd79bce8SPatrick Williams ptr->percentComplete; 4897e860f15SJohn Edward Broadbent }); 49046229577SJames Feist } 49146229577SJames Feist 4927e860f15SJohn Edward Broadbent inline void requestRoutesTaskCollection(App& app) 49346229577SJames Feist { 4947e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/TaskService/Tasks/") 495ed398213SEd Tanous .privileges(redfish::privileges::getTaskCollection) 4967e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::get)( 49745ca1b86SEd Tanous [&app](const crow::Request& req, 4987e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 4993ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 50045ca1b86SEd Tanous { 50145ca1b86SEd Tanous return; 50245ca1b86SEd Tanous } 50346229577SJames Feist asyncResp->res.jsonValue["@odata.type"] = 50446229577SJames Feist "#TaskCollection.TaskCollection"; 505bd79bce8SPatrick Williams asyncResp->res.jsonValue["@odata.id"] = 506bd79bce8SPatrick Williams "/redfish/v1/TaskService/Tasks"; 50746229577SJames Feist asyncResp->res.jsonValue["Name"] = "Task Collection"; 508bd79bce8SPatrick Williams asyncResp->res.jsonValue["Members@odata.count"] = 509bd79bce8SPatrick Williams task::tasks.size(); 51046229577SJames Feist nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 51146229577SJames Feist members = nlohmann::json::array(); 51246229577SJames Feist 51346229577SJames Feist for (const std::shared_ptr<task::TaskData>& task : task::tasks) 51446229577SJames Feist { 51546229577SJames Feist if (task == nullptr) 51646229577SJames Feist { 51746229577SJames Feist continue; // shouldn't be possible 51846229577SJames Feist } 519613dabeaSEd Tanous nlohmann::json::object_t member; 520ef4c65b7SEd Tanous member["@odata.id"] = 521ef4c65b7SEd Tanous boost::urls::format("/redfish/v1/TaskService/Tasks/{}", 522eddfc437SWilly Tu std::to_string(task->index)); 523613dabeaSEd Tanous members.emplace_back(std::move(member)); 52446229577SJames Feist } 5257e860f15SJohn Edward Broadbent }); 52646229577SJames Feist } 52746229577SJames Feist 5287e860f15SJohn Edward Broadbent inline void requestRoutesTaskService(App& app) 52946229577SJames Feist { 5307e860f15SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/TaskService/") 531ed398213SEd Tanous .privileges(redfish::privileges::getTaskService) 5327e860f15SJohn Edward Broadbent .methods(boost::beast::http::verb::get)( 53345ca1b86SEd Tanous [&app](const crow::Request& req, 5347e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 5353ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 53645ca1b86SEd Tanous { 53745ca1b86SEd Tanous return; 53845ca1b86SEd Tanous } 53946229577SJames Feist asyncResp->res.jsonValue["@odata.type"] = 54046229577SJames Feist "#TaskService.v1_1_4.TaskService"; 541bd79bce8SPatrick Williams asyncResp->res.jsonValue["@odata.id"] = 542bd79bce8SPatrick Williams "/redfish/v1/TaskService"; 54346229577SJames Feist asyncResp->res.jsonValue["Name"] = "Task Service"; 54446229577SJames Feist asyncResp->res.jsonValue["Id"] = "TaskService"; 5457e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["DateTime"] = 5462b82937eSEd Tanous redfish::time_utils::getDateTimeOffsetNow().first; 547539d8c6bSEd Tanous asyncResp->res.jsonValue["CompletedTaskOverWritePolicy"] = 548539d8c6bSEd Tanous task_service::OverWritePolicy::Oldest; 54946229577SJames Feist 550bd79bce8SPatrick Williams asyncResp->res.jsonValue["LifeCycleEventOnTaskStateChange"] = 551bd79bce8SPatrick Williams true; 55246229577SJames Feist 553bd79bce8SPatrick Williams asyncResp->res.jsonValue["Status"]["State"] = 554bd79bce8SPatrick Williams resource::State::Enabled; 55546229577SJames Feist asyncResp->res.jsonValue["ServiceEnabled"] = true; 5561476687dSEd Tanous asyncResp->res.jsonValue["Tasks"]["@odata.id"] = 5571476687dSEd Tanous "/redfish/v1/TaskService/Tasks"; 558*08fad5d9SCorey Ethington 559*08fad5d9SCorey Ethington etag_utils::setEtagOmitDateTimeHandler(asyncResp); 5607e860f15SJohn Edward Broadbent }); 56146229577SJames Feist } 56246229577SJames Feist 56346229577SJames Feist } // namespace redfish 564