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