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