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