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 "types.hpp" 18 19 #include <boost/algorithm/string/predicate.hpp> 20 #include <boost/algorithm/string/replace.hpp> 21 #include <boost/asio/post.hpp> 22 #include <boost/container/flat_set.hpp> 23 #include <cold_redundancy.hpp> 24 #include <phosphor-logging/elog-errors.hpp> 25 #include <sdbusplus/asio/connection.hpp> 26 #include <sdbusplus/asio/object_server.hpp> 27 #include <sdbusplus/bus/match.hpp> 28 29 #include <array> 30 #include <filesystem> 31 #include <fstream> 32 #include <iostream> 33 #include <regex> 34 35 namespace 36 { 37 constexpr const std::array<const char*, 1> psuInterfaceTypes = { 38 "xyz.openbmc_project.Configuration.pmbus"}; 39 std::string inventoryPath = std::string(INVENTORY_OBJ_PATH) + "/system"; 40 const constexpr char* eventPath = "/xyz/openbmc_project/State/Decorator"; 41 std::vector<std::unique_ptr<PowerSupply>> powerSupplies; 42 } // namespace 43 44 ColdRedundancy::ColdRedundancy( 45 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer, 46 std::shared_ptr<sdbusplus::asio::connection>& systemBus) : 47 filterTimer(io), systemBus(systemBus) 48 { 49 post(io, 50 [this, &io, &objectServer, &systemBus]() { createPSU(systemBus); }); 51 std::function<void(sdbusplus::message_t&)> eventHandler = 52 [this, &io, &objectServer, &systemBus](sdbusplus::message_t& message) { 53 if (message.is_method_error()) 54 { 55 std::cerr << "callback method error\n"; 56 return; 57 } 58 filterTimer.expires_after(std::chrono::seconds(1)); 59 filterTimer.async_wait([this, &io, &objectServer, &systemBus]( 60 const boost::system::error_code& ec) { 61 if (ec == boost::asio::error::operation_aborted) 62 { 63 return; 64 } 65 else if (ec) 66 { 67 std::cerr << "timer error\n"; 68 } 69 createPSU(systemBus); 70 }); 71 }; 72 73 std::function<void(sdbusplus::message_t&)> eventCollect = 74 [&](sdbusplus::message_t& message) { 75 std::string objectName; 76 boost::container::flat_map<std::string, std::variant<bool>> values; 77 std::string path = message.get_path(); 78 std::size_t slantingPos = path.find_last_of("/\\"); 79 if ((slantingPos == std::string::npos) || 80 ((slantingPos + 1) >= path.size())) 81 { 82 std::cerr << "Unable to get PSU state name from path\n"; 83 return; 84 } 85 std::string statePSUName = path.substr(slantingPos + 1); 86 87 std::size_t hypenPos = statePSUName.find("_"); 88 if (hypenPos == std::string::npos) 89 { 90 std::cerr << "Unable to get PSU name from PSU path\n"; 91 return; 92 } 93 std::string psuName = statePSUName.substr(0, hypenPos); 94 95 try 96 { 97 message.read(objectName, values); 98 } 99 catch (const sdbusplus::exception_t& e) 100 { 101 std::cerr << "Failed to read message from PSU Event\n"; 102 return; 103 } 104 105 for (auto& psu : powerSupplies) 106 { 107 if (psu->name != psuName) 108 { 109 continue; 110 } 111 112 std::string psuEventName = "functional"; 113 auto findEvent = values.find(psuEventName); 114 if (findEvent != values.end()) 115 { 116 bool* functional = std::get_if<bool>(&(findEvent->second)); 117 if (functional == nullptr) 118 { 119 std::cerr << "Unable to get valid functional status\n"; 120 continue; 121 } 122 if (*functional) 123 { 124 psu->state = CR::PSUState::normal; 125 } 126 else 127 { 128 psu->state = CR::PSUState::acLost; 129 } 130 } 131 } 132 }; 133 134 using namespace sdbusplus::bus::match::rules; 135 for (const char* type : psuInterfaceTypes) 136 { 137 auto match = std::make_unique<sdbusplus::bus::match_t>( 138 static_cast<sdbusplus::bus_t&>(*systemBus), 139 type::signal() + member("PropertiesChanged") + 140 path_namespace(inventoryPath) + arg0namespace(type), 141 eventHandler); 142 matches.emplace_back(std::move(match)); 143 } 144 145 for (const char* eventType : psuEventInterface) 146 { 147 auto eventMatch = std::make_unique<sdbusplus::bus::match_t>( 148 static_cast<sdbusplus::bus_t&>(*systemBus), 149 type::signal() + member("PropertiesChanged") + 150 path_namespace(eventPath) + arg0namespace(eventType), 151 eventCollect); 152 matches.emplace_back(std::move(eventMatch)); 153 } 154 } 155 156 static const constexpr int psuDepth = 3; 157 // Check PSU information from entity-manager D-Bus interface and use the bus 158 // address to create PSU Class for cold redundancy. 159 void ColdRedundancy::createPSU( 160 std::shared_ptr<sdbusplus::asio::connection>& conn) 161 { 162 numberOfPSU = 0; 163 powerSupplies.clear(); 164 165 // call mapper to get matched obj paths 166 conn->async_method_call( 167 [this, &conn](const boost::system::error_code ec, 168 CR::GetSubTreeType subtree) { 169 if (ec) 170 { 171 std::cerr << "Exception happened when communicating to " 172 "ObjectMapper\n"; 173 return; 174 } 175 for (const auto& object : subtree) 176 { 177 std::string pathName = object.first; 178 for (const auto& serviceIface : object.second) 179 { 180 std::string serviceName = serviceIface.first; 181 for (const auto& interface : serviceIface.second) 182 { 183 // only get property of matched interface 184 bool isIfaceMatched = false; 185 for (const auto& type : psuInterfaceTypes) 186 { 187 if (type == interface) 188 { 189 isIfaceMatched = true; 190 break; 191 } 192 } 193 if (!isIfaceMatched) 194 continue; 195 196 conn->async_method_call( 197 [this, &conn, 198 interface](const boost::system::error_code ec, 199 CR::PropertyMapType propMap) { 200 if (ec) 201 { 202 std::cerr 203 << "Exception happened when get all " 204 "properties\n"; 205 return; 206 } 207 208 auto configName = 209 std::get_if<std::string>(&propMap["Name"]); 210 if (configName == nullptr) 211 { 212 std::cerr << "error finding necessary " 213 "entry in configuration\n"; 214 return; 215 } 216 217 auto configBus = 218 std::get_if<uint64_t>(&propMap["Bus"]); 219 auto configAddress = 220 std::get_if<uint64_t>(&propMap["Address"]); 221 222 if (configBus == nullptr || 223 configAddress == nullptr) 224 { 225 std::cerr << "error finding necessary " 226 "entry in configuration\n"; 227 return; 228 } 229 for (auto& psu : powerSupplies) 230 { 231 if ((static_cast<uint8_t>(*configBus) == 232 psu->bus) && 233 (static_cast<uint8_t>(*configAddress) == 234 psu->address)) 235 { 236 return; 237 } 238 } 239 240 uint8_t order = 0; 241 242 powerSupplies.emplace_back( 243 std::make_unique<PowerSupply>( 244 *configName, 245 static_cast<uint8_t>(*configBus), 246 static_cast<uint8_t>(*configAddress), 247 order, conn)); 248 249 numberOfPSU++; 250 std::vector<uint8_t> orders = {}; 251 for (auto& psu : powerSupplies) 252 { 253 orders.push_back(psu->order); 254 } 255 }, 256 serviceName.c_str(), pathName.c_str(), 257 "org.freedesktop.DBus.Properties", "GetAll", 258 interface); 259 } 260 } 261 } 262 }, 263 "xyz.openbmc_project.ObjectMapper", 264 "/xyz/openbmc_project/object_mapper", 265 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 266 "/xyz/openbmc_project/inventory/system", psuDepth, psuInterfaceTypes); 267 } 268 269 PowerSupply::PowerSupply( 270 std::string& name, uint8_t bus, uint8_t address, uint8_t order, 271 const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection) : 272 name(name), bus(bus), address(address), order(order) 273 { 274 CR::getPSUEvent(dbusConnection, name, state); 275 } 276