blob: 2409e0a77b0a2154778139b82d8e8881bc054c65 [file] [log] [blame]
Cheng C Yange83604b2020-01-09 09:41:47 +08001/*
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
Cheng C Yange83604b2020-01-09 09:41:47 +080019#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>
Cheng C Yange83604b2020-01-09 09:41:47 +080024#include <phosphor-logging/elog-errors.hpp>
Cheng C Yange83604b2020-01-09 09:41:47 +080025#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
Bob King0dcbdf52020-01-20 17:19:39 +080030#include <array>
31#include <filesystem>
32#include <fstream>
33#include <iostream>
34#include <regex>
35
Cheng C Yange83604b2020-01-09 09:41:47 +080036namespace
37{
38constexpr const std::array<const char*, 1> psuInterfaceTypes = {
39 "xyz.openbmc_project.Configuration.pmbus"};
40std::string inventoryPath = std::string(INVENTORY_OBJ_PATH) + "/system";
41const constexpr char* eventPath = "/xyz/openbmc_project/State/Decorator";
42std::vector<std::unique_ptr<PowerSupply>> powerSupplies;
43} // namespace
44
45ColdRedundancy::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
159static 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.
162void 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
272PowerSupply::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}