xref: /openbmc/phosphor-power/cold-redundancy/cold_redundancy.cpp (revision 5ed2eb4c375c7037c95a4a635c180af60eac28e5)
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 
ColdRedundancy(boost::asio::io_service & io,std::shared_ptr<sdbusplus::asio::connection> & systemBus)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.
createPSU(std::shared_ptr<sdbusplus::asio::connection> & conn)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 
PowerSupply(std::string & name,uint8_t bus,uint8_t address,uint8_t order,const std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)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