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