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