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 17 #include "dbuspassiveredundancy.hpp" 18 19 #include <sdbusplus/bus.hpp> 20 #include <sdbusplus/bus/match.hpp> 21 22 #include <iostream> 23 #include <set> 24 #include <unordered_map> 25 #include <variant> 26 27 namespace properties 28 { 29 30 constexpr const char* interface = "org.freedesktop.DBus.Properties"; 31 constexpr const char* get = "Get"; 32 constexpr const char* getAll = "GetAll"; 33 34 } // namespace properties 35 36 namespace redundancy 37 { 38 39 constexpr const char* collection = "Collection"; 40 constexpr const char* status = "Status"; 41 constexpr const char* interface = "xyz.openbmc_project.Control.FanRedundancy"; 42 43 } // namespace redundancy 44 45 DbusPassiveRedundancy::DbusPassiveRedundancy(sdbusplus::bus::bus& bus) : 46 match(bus, 47 "type='signal',member='PropertiesChanged',arg0namespace='" + 48 std::string(redundancy::interface) + "'", 49 std::move([this](sdbusplus::message::message& message) { 50 std::string objectName; 51 std::unordered_map< 52 std::string, 53 std::variant<std::string, std::vector<std::string>>> 54 result; 55 try 56 { 57 message.read(objectName, result); 58 } 59 catch (sdbusplus::exception_t&) 60 { 61 std::cerr << "Error reading match data"; 62 return; 63 } 64 auto findStatus = result.find("Status"); 65 if (findStatus == result.end()) 66 { 67 return; 68 } 69 std::string status = std::get<std::string>(findStatus->second); 70 71 auto methodCall = passiveBus.new_method_call( 72 message.get_sender(), message.get_path(), 73 properties::interface, properties::get); 74 methodCall.append(redundancy::interface, redundancy::collection); 75 std::variant<std::vector<std::string>> collection; 76 77 try 78 { 79 auto reply = passiveBus.call(methodCall); 80 reply.read(collection); 81 } 82 catch (sdbusplus::exception_t&) 83 { 84 std::cerr << "Error reading match data"; 85 return; 86 } 87 88 auto data = std::get<std::vector<std::string>>(collection); 89 if (status.rfind("Failed") != std::string::npos) 90 { 91 failed.insert(data.begin(), data.end()); 92 } 93 else 94 { 95 for (const auto& d : data) 96 { 97 failed.erase(d); 98 } 99 } 100 })), 101 passiveBus(bus) 102 { 103 populateFailures(); 104 } 105 106 void DbusPassiveRedundancy::populateFailures(void) 107 { 108 auto mapper = passiveBus.new_method_call( 109 "xyz.openbmc_project.ObjectMapper", 110 "/xyz/openbmc_project/object_mapper", 111 "xyz.openbmc_project.ObjectMapper", "GetSubTree"); 112 mapper.append("/", 0, std::array<const char*, 1>{redundancy::interface}); 113 std::unordered_map< 114 std::string, std::unordered_map<std::string, std::vector<std::string>>> 115 respData; 116 try 117 { 118 auto resp = passiveBus.call(mapper); 119 resp.read(respData); 120 } 121 catch (sdbusplus::exception_t&) 122 { 123 std::cerr << "Populate Failures Mapper Error\n"; 124 return; 125 } 126 127 /* 128 * The subtree response looks like: 129 * {path : 130 * {busname: 131 * {interface, interface, interface, ...} 132 * } 133 * } 134 * 135 * This loops through this structure to pre-poulate the already failed items 136 */ 137 138 for (const auto& [path, interfaceDict] : respData) 139 { 140 for (const auto& [owner, _] : interfaceDict) 141 { 142 auto call = passiveBus.new_method_call(owner.c_str(), path.c_str(), 143 properties::interface, 144 properties::getAll); 145 call.append(redundancy::interface); 146 147 std::unordered_map< 148 std::string, 149 std::variant<std::string, std::vector<std::string>>> 150 getAll; 151 try 152 { 153 auto data = passiveBus.call(call); 154 data.read(getAll); 155 } 156 catch (sdbusplus::exception_t&) 157 { 158 std::cerr << "Populate Failures Mapper Error\n"; 159 return; 160 } 161 std::string status = 162 std::get<std::string>(getAll[redundancy::status]); 163 if (status.rfind("Failed") == std::string::npos) 164 { 165 continue; 166 } 167 std::vector<std::string> collection = 168 std::get<std::vector<std::string>>( 169 getAll[redundancy::collection]); 170 failed.insert(collection.begin(), collection.end()); 171 } 172 } 173 } 174 175 const std::set<std::string>& DbusPassiveRedundancy::getFailed() 176 { 177 return failed; 178 }