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