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