1 /* 2 // Copyright (c) 2020 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 #include "node.hpp" 18 #include "registries.hpp" 19 #include "registries/base_message_registry.hpp" 20 #include "registries/openbmc_message_registry.hpp" 21 22 #include <sys/inotify.h> 23 24 #include <boost/container/flat_map.hpp> 25 #include <cstdlib> 26 #include <ctime> 27 #include <error_messages.hpp> 28 #include <http_client.hpp> 29 #include <memory> 30 #include <utils/json_utils.hpp> 31 #include <variant> 32 33 namespace redfish 34 { 35 36 using ReadingsObjType = 37 std::vector<std::tuple<std::string, std::string, double, std::string>>; 38 39 static constexpr const char* eventFormatType = "Event"; 40 static constexpr const char* metricReportFormatType = "MetricReport"; 41 42 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 43 constexpr const char* redfishEventLogFile = "/var/log/redfish"; 44 constexpr const uint32_t inotifyFileAction = IN_MODIFY; 45 std::shared_ptr<boost::asio::posix::stream_descriptor> inotifyConn = nullptr; 46 47 // <ID, timestamp, RedfishLogId, registryPrefix, MessageId, MessageArgs> 48 using EventLogObjectsType = 49 std::tuple<std::string, std::string, std::string, std::string, std::string, 50 boost::beast::span<std::string>>; 51 52 namespace message_registries 53 { 54 static const Message* 55 getMsgFromRegistry(const std::string& messageKey, 56 const boost::beast::span<const MessageEntry>& registry) 57 { 58 boost::beast::span<const MessageEntry>::const_iterator messageIt = 59 std::find_if(registry.cbegin(), registry.cend(), 60 [&messageKey](const MessageEntry& messageEntry) { 61 return !messageKey.compare(messageEntry.first); 62 }); 63 if (messageIt != registry.cend()) 64 { 65 return &messageIt->second; 66 } 67 68 return nullptr; 69 } 70 71 static const Message* formatMessage(const std::string_view& messageID) 72 { 73 // Redfish MessageIds are in the form 74 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 75 // the right Message 76 std::vector<std::string> fields; 77 fields.reserve(4); 78 boost::split(fields, messageID, boost::is_any_of(".")); 79 if (fields.size() != 4) 80 { 81 return nullptr; 82 } 83 std::string& registryName = fields[0]; 84 std::string& messageKey = fields[3]; 85 86 // Find the right registry and check it for the MessageKey 87 if (std::string(base::header.registryPrefix) == registryName) 88 { 89 return getMsgFromRegistry( 90 messageKey, boost::beast::span<const MessageEntry>(base::registry)); 91 } 92 if (std::string(openbmc::header.registryPrefix) == registryName) 93 { 94 return getMsgFromRegistry( 95 messageKey, 96 boost::beast::span<const MessageEntry>(openbmc::registry)); 97 } 98 return nullptr; 99 } 100 } // namespace message_registries 101 102 namespace event_log 103 { 104 bool getUniqueEntryID(const std::string& logEntry, std::string& entryID, 105 const bool firstEntry = true) 106 { 107 static time_t prevTs = 0; 108 static int index = 0; 109 if (firstEntry) 110 { 111 prevTs = 0; 112 } 113 114 // Get the entry timestamp 115 std::time_t curTs = 0; 116 std::tm timeStruct = {}; 117 std::istringstream entryStream(logEntry); 118 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 119 { 120 curTs = std::mktime(&timeStruct); 121 if (curTs == -1) 122 { 123 return false; 124 } 125 } 126 // If the timestamp isn't unique, increment the index 127 index = (curTs == prevTs) ? index + 1 : 0; 128 129 // Save the timestamp 130 prevTs = curTs; 131 132 entryID = std::to_string(curTs); 133 if (index > 0) 134 { 135 entryID += "_" + std::to_string(index); 136 } 137 return true; 138 } 139 140 int getEventLogParams(const std::string& logEntry, std::string& timestamp, 141 std::string& messageID, 142 boost::beast::span<std::string>& messageArgs) 143 { 144 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 145 // First get the Timestamp 146 size_t space = logEntry.find_first_of(" "); 147 if (space == std::string::npos) 148 { 149 return -EINVAL; 150 } 151 timestamp = logEntry.substr(0, space); 152 // Then get the log contents 153 size_t entryStart = logEntry.find_first_not_of(" ", space); 154 if (entryStart == std::string::npos) 155 { 156 return -EINVAL; 157 } 158 std::string_view entry(logEntry); 159 entry.remove_prefix(entryStart); 160 // Use split to separate the entry into its fields 161 std::vector<std::string> logEntryFields; 162 boost::split(logEntryFields, entry, boost::is_any_of(","), 163 boost::token_compress_on); 164 // We need at least a MessageId to be valid 165 if (logEntryFields.size() < 1) 166 { 167 return -EINVAL; 168 } 169 messageID = logEntryFields[0]; 170 171 // Get the MessageArgs from the log if there are any 172 if (logEntryFields.size() > 1) 173 { 174 std::string& messageArgsStart = logEntryFields[1]; 175 // If the first string is empty, assume there are no MessageArgs 176 std::size_t messageArgsSize = 0; 177 if (!messageArgsStart.empty()) 178 { 179 messageArgsSize = logEntryFields.size() - 1; 180 } 181 182 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize); 183 } 184 185 return 0; 186 } 187 188 void getRegistryAndMessageKey(const std::string& messageID, 189 std::string& registryName, 190 std::string& messageKey) 191 { 192 // Redfish MessageIds are in the form 193 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 194 // the right Message 195 std::vector<std::string> fields; 196 fields.reserve(4); 197 boost::split(fields, messageID, boost::is_any_of(".")); 198 if (fields.size() == 4) 199 { 200 registryName = fields[0]; 201 messageKey = fields[3]; 202 } 203 } 204 205 int formatEventLogEntry(const std::string& logEntryID, 206 const std::string& messageID, 207 const boost::beast::span<std::string>& messageArgs, 208 std::string timestamp, const std::string customText, 209 nlohmann::json& logEntryJson) 210 { 211 // Get the Message from the MessageRegistry 212 const message_registries::Message* message = 213 message_registries::formatMessage(messageID); 214 215 std::string msg; 216 std::string severity; 217 if (message != nullptr) 218 { 219 msg = message->message; 220 severity = message->severity; 221 } 222 223 // Fill the MessageArgs into the Message 224 int i = 0; 225 for (const std::string& messageArg : messageArgs) 226 { 227 std::string argStr = "%" + std::to_string(++i); 228 size_t argPos = msg.find(argStr); 229 if (argPos != std::string::npos) 230 { 231 msg.replace(argPos, argStr.length(), messageArg); 232 } 233 } 234 235 // Get the Created time from the timestamp. The log timestamp is in 236 // RFC3339 format which matches the Redfish format except for the 237 // fractional seconds between the '.' and the '+', so just remove them. 238 std::size_t dot = timestamp.find_first_of("."); 239 std::size_t plus = timestamp.find_first_of("+"); 240 if (dot != std::string::npos && plus != std::string::npos) 241 { 242 timestamp.erase(dot, plus - dot); 243 } 244 245 // Fill in the log entry with the gathered data 246 logEntryJson = {{"EventId", logEntryID}, 247 {"EventType", "Event"}, 248 {"Severity", std::move(severity)}, 249 {"Message", std::move(msg)}, 250 {"MessageId", std::move(messageID)}, 251 {"MessageArgs", std::move(messageArgs)}, 252 {"EventTimestamp", std::move(timestamp)}, 253 {"Context", customText}}; 254 return 0; 255 } 256 257 } // namespace event_log 258 #endif 259 260 class Subscription 261 { 262 public: 263 std::string id; 264 std::string destinationUrl; 265 std::string protocol; 266 std::string retryPolicy; 267 std::string customText; 268 std::string eventFormatType; 269 std::string subscriptionType; 270 std::vector<std::string> registryMsgIds; 271 std::vector<std::string> registryPrefixes; 272 std::vector<nlohmann::json> httpHeaders; // key-value pair 273 std::vector<nlohmann::json> metricReportDefinitions; 274 275 Subscription(const Subscription&) = delete; 276 Subscription& operator=(const Subscription&) = delete; 277 Subscription(Subscription&&) = delete; 278 Subscription& operator=(Subscription&&) = delete; 279 280 Subscription(const std::string& inHost, const std::string& inPort, 281 const std::string& inPath, const std::string& inUriProto) : 282 eventSeqNum(1), 283 host(inHost), port(inPort), path(inPath), uriProto(inUriProto) 284 { 285 conn = std::make_shared<crow::HttpClient>( 286 crow::connections::systemBus->get_io_context(), host, port, path); 287 } 288 ~Subscription() 289 { 290 } 291 292 void sendEvent(const std::string& msg) 293 { 294 std::vector<std::pair<std::string, std::string>> reqHeaders; 295 for (const auto& header : httpHeaders) 296 { 297 for (const auto& item : header.items()) 298 { 299 std::string key = item.key(); 300 std::string val = item.value(); 301 reqHeaders.emplace_back(std::pair(key, val)); 302 } 303 } 304 conn->setHeaders(reqHeaders); 305 conn->sendData(msg); 306 } 307 308 void sendTestEventLog() 309 { 310 nlohmann::json logEntryArray; 311 logEntryArray.push_back({}); 312 nlohmann::json& logEntryJson = logEntryArray.back(); 313 314 logEntryJson = {{"EventId", "TestID"}, 315 {"EventType", "Event"}, 316 {"Severity", "OK"}, 317 {"Message", "Generated test event"}, 318 {"MessageId", "OpenBMC.0.1.TestEventLog"}, 319 {"MessageArgs", nlohmann::json::array()}, 320 {"EventTimestamp", crow::utility::dateTimeNow()}, 321 {"Context", customText}}; 322 323 nlohmann::json msg = {{"@odata.type", "#Event.v1_4_0.Event"}, 324 {"Id", std::to_string(eventSeqNum)}, 325 {"Name", "Event Log"}, 326 {"Events", logEntryArray}}; 327 328 this->sendEvent(msg.dump()); 329 this->eventSeqNum++; 330 } 331 332 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 333 void filterAndSendEventLogs( 334 const std::vector<EventLogObjectsType>& eventRecords) 335 { 336 nlohmann::json logEntryArray; 337 for (const EventLogObjectsType& logEntry : eventRecords) 338 { 339 const std::string& idStr = std::get<0>(logEntry); 340 const std::string& timestamp = std::get<1>(logEntry); 341 const std::string& messageID = std::get<2>(logEntry); 342 const std::string& registryName = std::get<3>(logEntry); 343 const std::string& messageKey = std::get<4>(logEntry); 344 const boost::beast::span<std::string>& messageArgs = 345 std::get<5>(logEntry); 346 347 // If registryPrefixes list is empty, don't filter events 348 // send everything. 349 if (registryPrefixes.size()) 350 { 351 auto obj = std::find(registryPrefixes.begin(), 352 registryPrefixes.end(), registryName); 353 if (obj == registryPrefixes.end()) 354 { 355 continue; 356 } 357 } 358 359 // If registryMsgIds list is empty, don't filter events 360 // send everything. 361 if (registryMsgIds.size()) 362 { 363 auto obj = std::find(registryMsgIds.begin(), 364 registryMsgIds.end(), messageKey); 365 if (obj == registryMsgIds.end()) 366 { 367 continue; 368 } 369 } 370 371 logEntryArray.push_back({}); 372 nlohmann::json& bmcLogEntry = logEntryArray.back(); 373 if (event_log::formatEventLogEntry(idStr, messageID, messageArgs, 374 timestamp, customText, 375 bmcLogEntry) != 0) 376 { 377 BMCWEB_LOG_DEBUG << "Read eventLog entry failed"; 378 continue; 379 } 380 } 381 382 if (logEntryArray.size() < 1) 383 { 384 BMCWEB_LOG_DEBUG << "No log entries available to be transferred."; 385 return; 386 } 387 388 nlohmann::json msg = {{"@odata.type", "#Event.v1_4_0.Event"}, 389 {"Id", std::to_string(eventSeqNum)}, 390 {"Name", "Event Log"}, 391 {"Events", logEntryArray}}; 392 393 this->sendEvent(msg.dump()); 394 this->eventSeqNum++; 395 } 396 #endif 397 398 void filterAndSendReports(const std::string& id, 399 const std::string& readingsTs, 400 const ReadingsObjType& readings) 401 { 402 std::string metricReportDef = 403 "/redfish/v1/TelemetryService/MetricReportDefinitions/" + id; 404 405 // Empty list means no filter. Send everything. 406 if (metricReportDefinitions.size()) 407 { 408 if (std::find(metricReportDefinitions.begin(), 409 metricReportDefinitions.end(), 410 metricReportDef) == metricReportDefinitions.end()) 411 { 412 return; 413 } 414 } 415 416 nlohmann::json metricValuesArray = nlohmann::json::array(); 417 for (const auto& it : readings) 418 { 419 metricValuesArray.push_back({}); 420 nlohmann::json& entry = metricValuesArray.back(); 421 422 entry = {{"MetricId", std::get<0>(it)}, 423 {"MetricProperty", std::get<1>(it)}, 424 {"MetricValue", std::to_string(std::get<2>(it))}, 425 {"Timestamp", std::get<3>(it)}}; 426 } 427 428 nlohmann::json msg = { 429 {"@odata.id", "/redfish/v1/TelemetryService/MetricReports/" + id}, 430 {"@odata.type", "#MetricReport.v1_3_0.MetricReport"}, 431 {"Id", id}, 432 {"Name", id}, 433 {"Timestamp", readingsTs}, 434 {"MetricReportDefinition", {{"@odata.id", metricReportDef}}}, 435 {"MetricValues", metricValuesArray}}; 436 437 this->sendEvent(msg.dump()); 438 } 439 440 private: 441 uint64_t eventSeqNum; 442 std::string host; 443 std::string port; 444 std::string path; 445 std::string uriProto; 446 std::shared_ptr<crow::HttpClient> conn; 447 }; 448 449 class EventServiceManager 450 { 451 private: 452 EventServiceManager(const EventServiceManager&) = delete; 453 EventServiceManager& operator=(const EventServiceManager&) = delete; 454 EventServiceManager(EventServiceManager&&) = delete; 455 EventServiceManager& operator=(EventServiceManager&&) = delete; 456 457 EventServiceManager() : noOfMetricReportSubscribers(0) 458 { 459 // TODO: Read the persistent data from store and populate. 460 // Populating with default. 461 enabled = true; 462 retryAttempts = 3; 463 retryTimeoutInterval = 30; // seconds 464 } 465 466 std::string lastEventTStr; 467 size_t noOfMetricReportSubscribers; 468 std::shared_ptr<sdbusplus::bus::match::match> matchTelemetryMonitor; 469 boost::container::flat_map<std::string, std::shared_ptr<Subscription>> 470 subscriptionsMap; 471 472 public: 473 bool enabled; 474 uint32_t retryAttempts; 475 uint32_t retryTimeoutInterval; 476 477 static EventServiceManager& getInstance() 478 { 479 static EventServiceManager handler; 480 return handler; 481 } 482 483 void updateSubscriptionData() 484 { 485 // Persist the config and subscription data. 486 // TODO: subscriptionsMap & configData need to be 487 // written to Persist store. 488 return; 489 } 490 491 std::shared_ptr<Subscription> getSubscription(const std::string& id) 492 { 493 auto obj = subscriptionsMap.find(id); 494 if (obj == subscriptionsMap.end()) 495 { 496 BMCWEB_LOG_ERROR << "No subscription exist with ID:" << id; 497 return nullptr; 498 } 499 std::shared_ptr<Subscription> subValue = obj->second; 500 return subValue; 501 } 502 503 std::string addSubscription(const std::shared_ptr<Subscription> subValue) 504 { 505 std::srand(static_cast<uint32_t>(std::time(0))); 506 std::string id; 507 508 int retry = 3; 509 while (retry) 510 { 511 id = std::to_string(std::rand()); 512 auto inserted = subscriptionsMap.insert(std::pair(id, subValue)); 513 if (inserted.second) 514 { 515 break; 516 } 517 --retry; 518 }; 519 520 if (retry <= 0) 521 { 522 BMCWEB_LOG_ERROR << "Failed to generate random number"; 523 return std::string(""); 524 } 525 526 if (subValue->eventFormatType == metricReportFormatType) 527 { 528 // If it is first entry, Register Metrics report signal. 529 if ((++noOfMetricReportSubscribers == 1)) 530 { 531 registerMetricReportSignal(); 532 } 533 } 534 535 updateSubscriptionData(); 536 537 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 538 if (lastEventTStr.empty()) 539 { 540 cacheLastEventTimestamp(); 541 } 542 #endif 543 return id; 544 } 545 546 bool isSubscriptionExist(const std::string& id) 547 { 548 auto obj = subscriptionsMap.find(id); 549 if (obj == subscriptionsMap.end()) 550 { 551 return false; 552 } 553 return true; 554 } 555 556 void deleteSubscription(const std::string& id) 557 { 558 auto obj = subscriptionsMap.find(id); 559 if (obj != subscriptionsMap.end()) 560 { 561 std::shared_ptr<Subscription> entry = obj->second; 562 if (entry->eventFormatType == metricReportFormatType) 563 { 564 if ((--noOfMetricReportSubscribers == 0)) 565 { 566 unregisterMetricReportSignal(); 567 } 568 } 569 570 subscriptionsMap.erase(obj); 571 updateSubscriptionData(); 572 } 573 } 574 575 size_t getNumberOfSubscriptions() 576 { 577 return subscriptionsMap.size(); 578 } 579 580 std::vector<std::string> getAllIDs() 581 { 582 std::vector<std::string> idList; 583 for (const auto& it : subscriptionsMap) 584 { 585 idList.emplace_back(it.first); 586 } 587 return idList; 588 } 589 590 bool isDestinationExist(const std::string& destUrl) 591 { 592 for (const auto& it : subscriptionsMap) 593 { 594 std::shared_ptr<Subscription> entry = it.second; 595 if (entry->destinationUrl == destUrl) 596 { 597 BMCWEB_LOG_ERROR << "Destination exist already" << destUrl; 598 return true; 599 } 600 } 601 return false; 602 } 603 604 void sendTestEventLog() 605 { 606 for (const auto& it : this->subscriptionsMap) 607 { 608 std::shared_ptr<Subscription> entry = it.second; 609 entry->sendTestEventLog(); 610 } 611 } 612 613 #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES 614 void cacheLastEventTimestamp() 615 { 616 std::ifstream logStream(redfishEventLogFile); 617 if (!logStream.good()) 618 { 619 BMCWEB_LOG_ERROR << " Redfish log file open failed \n"; 620 return; 621 } 622 std::string logEntry; 623 while (std::getline(logStream, logEntry)) 624 { 625 size_t space = logEntry.find_first_of(" "); 626 if (space == std::string::npos) 627 { 628 // Shouldn't enter here but lets skip it. 629 BMCWEB_LOG_DEBUG << "Invalid log entry found."; 630 continue; 631 } 632 lastEventTStr = logEntry.substr(0, space); 633 } 634 BMCWEB_LOG_DEBUG << "Last Event time stamp set: " << lastEventTStr; 635 } 636 637 void readEventLogsFromFile() 638 { 639 if (!getNumberOfSubscriptions()) 640 { 641 // no subscriptions. Just return. 642 BMCWEB_LOG_DEBUG << "No Subscriptions\n"; 643 return; 644 } 645 std::ifstream logStream(redfishEventLogFile); 646 if (!logStream.good()) 647 { 648 BMCWEB_LOG_ERROR << " Redfish log file open failed"; 649 return; 650 } 651 652 std::vector<EventLogObjectsType> eventRecords; 653 654 bool startLogCollection = false; 655 bool firstEntry = true; 656 657 std::string logEntry; 658 while (std::getline(logStream, logEntry)) 659 { 660 if (!startLogCollection) 661 { 662 if (boost::starts_with(logEntry, lastEventTStr)) 663 { 664 startLogCollection = true; 665 } 666 continue; 667 } 668 669 std::string idStr; 670 if (!event_log::getUniqueEntryID(logEntry, idStr, firstEntry)) 671 { 672 continue; 673 } 674 firstEntry = false; 675 676 std::string timestamp; 677 std::string messageID; 678 boost::beast::span<std::string> messageArgs; 679 if (event_log::getEventLogParams(logEntry, timestamp, messageID, 680 messageArgs) != 0) 681 { 682 BMCWEB_LOG_DEBUG << "Read eventLog entry params failed"; 683 continue; 684 } 685 686 std::string registryName; 687 std::string messageKey; 688 event_log::getRegistryAndMessageKey(messageID, registryName, 689 messageKey); 690 if (registryName.empty() || messageKey.empty()) 691 { 692 continue; 693 } 694 695 lastEventTStr = timestamp; 696 eventRecords.emplace_back(idStr, timestamp, messageID, registryName, 697 messageKey, messageArgs); 698 } 699 700 for (const auto& it : this->subscriptionsMap) 701 { 702 std::shared_ptr<Subscription> entry = it.second; 703 if (entry->eventFormatType == "Event") 704 { 705 entry->filterAndSendEventLogs(eventRecords); 706 } 707 } 708 } 709 710 static void watchRedfishEventLogFile() 711 { 712 if (inotifyConn == nullptr) 713 { 714 return; 715 } 716 717 static std::array<char, 1024> readBuffer; 718 719 inotifyConn->async_read_some( 720 boost::asio::buffer(readBuffer), 721 [&](const boost::system::error_code& ec, 722 const std::size_t& bytesTransferred) { 723 if (ec) 724 { 725 BMCWEB_LOG_ERROR << "Callback Error: " << ec.message(); 726 return; 727 } 728 std::size_t index = 0; 729 while ((index + sizeof(inotify_event)) <= bytesTransferred) 730 { 731 struct inotify_event event; 732 std::memcpy(&event, &readBuffer[index], 733 sizeof(inotify_event)); 734 if (event.mask == inotifyFileAction) 735 { 736 EventServiceManager::getInstance() 737 .readEventLogsFromFile(); 738 } 739 index += (sizeof(inotify_event) + event.len); 740 } 741 742 watchRedfishEventLogFile(); 743 }); 744 } 745 746 static int startEventLogMonitor(boost::asio::io_service& ioc) 747 { 748 inotifyConn = 749 std::make_shared<boost::asio::posix::stream_descriptor>(ioc); 750 int fd = inotify_init1(IN_NONBLOCK); 751 if (fd == -1) 752 { 753 BMCWEB_LOG_ERROR << "inotify_init1 failed."; 754 return -1; 755 } 756 auto wd = inotify_add_watch(fd, redfishEventLogFile, inotifyFileAction); 757 if (wd == -1) 758 { 759 BMCWEB_LOG_ERROR 760 << "inotify_add_watch failed for redfish log file."; 761 return -1; 762 } 763 764 // monitor redfish event log file 765 inotifyConn->assign(fd); 766 watchRedfishEventLogFile(); 767 768 return 0; 769 } 770 771 #endif 772 773 void getMetricReading(const std::string& service, 774 const std::string& objPath, const std::string& intf) 775 { 776 std::size_t found = objPath.find_last_of("/"); 777 if (found == std::string::npos) 778 { 779 BMCWEB_LOG_DEBUG << "Invalid objPath received"; 780 return; 781 } 782 783 std::string idStr = objPath.substr(found + 1); 784 if (idStr.empty()) 785 { 786 BMCWEB_LOG_DEBUG << "Invalid ID in objPath"; 787 return; 788 } 789 790 crow::connections::systemBus->async_method_call( 791 [idStr{std::move(idStr)}]( 792 const boost::system::error_code ec, 793 boost::container::flat_map< 794 std::string, std::variant<std::string, ReadingsObjType>>& 795 resp) { 796 if (ec) 797 { 798 BMCWEB_LOG_DEBUG 799 << "D-Bus call failed to GetAll metric readings."; 800 return; 801 } 802 803 const std::string* timestampPtr = 804 std::get_if<std::string>(&resp["Timestamp"]); 805 if (!timestampPtr) 806 { 807 BMCWEB_LOG_DEBUG << "Failed to Get timestamp."; 808 return; 809 } 810 811 ReadingsObjType* readingsPtr = 812 std::get_if<ReadingsObjType>(&resp["Readings"]); 813 if (!readingsPtr) 814 { 815 BMCWEB_LOG_DEBUG << "Failed to Get Readings property."; 816 return; 817 } 818 819 if (!readingsPtr->size()) 820 { 821 BMCWEB_LOG_DEBUG << "No metrics report to be transferred"; 822 return; 823 } 824 825 for (const auto& it : 826 EventServiceManager::getInstance().subscriptionsMap) 827 { 828 std::shared_ptr<Subscription> entry = it.second; 829 if (entry->eventFormatType == metricReportFormatType) 830 { 831 entry->filterAndSendReports(idStr, *timestampPtr, 832 *readingsPtr); 833 } 834 } 835 }, 836 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 837 intf); 838 } 839 840 void unregisterMetricReportSignal() 841 { 842 BMCWEB_LOG_DEBUG << "Metrics report signal - Unregister"; 843 matchTelemetryMonitor.reset(); 844 matchTelemetryMonitor = nullptr; 845 } 846 847 void registerMetricReportSignal() 848 { 849 if (matchTelemetryMonitor) 850 { 851 BMCWEB_LOG_DEBUG << "Metrics report signal - Already registered."; 852 return; 853 } 854 855 BMCWEB_LOG_DEBUG << "Metrics report signal - Register"; 856 std::string matchStr( 857 "type='signal',member='ReportUpdate', " 858 "interface='xyz.openbmc_project.MonitoringService.Report'"); 859 860 matchTelemetryMonitor = std::make_shared<sdbusplus::bus::match::match>( 861 *crow::connections::systemBus, matchStr, 862 [this](sdbusplus::message::message& msg) { 863 if (msg.is_method_error()) 864 { 865 BMCWEB_LOG_ERROR << "TelemetryMonitor Signal error"; 866 return; 867 } 868 869 std::string service = msg.get_sender(); 870 std::string objPath = msg.get_path(); 871 std::string intf = msg.get_interface(); 872 getMetricReading(service, objPath, intf); 873 }); 874 } 875 }; 876 877 } // namespace redfish 878