blob: faf035df2b6ae42bdb8b684111ea1ddc3809f4b9 [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
2 * Copyright © 2019 IBM 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 */
Matt Spinlercad9c2b2019-12-02 15:42:01 -060016#include "config.h"
17
Matt Spinlerc8705e22019-09-11 12:36:07 -050018#include "data_interface.hpp"
19
Matt Spinlerf61f2922020-06-23 11:32:49 -050020#include "util.hpp"
21
Vijay Lobo81b4dca2021-04-29 00:04:00 -050022#include <fmt/format.h>
23
Matt Spinlercad9c2b2019-12-02 15:42:01 -060024#include <fstream>
Vijay Lobo81b4dca2021-04-29 00:04:00 -050025#include <phosphor-logging/log.hpp>
Matt Spinlerf10068d2020-12-02 10:44:08 -060026#include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
Matt Spinlera7d9d962019-11-06 15:01:25 -060027
Matt Spinlerc8705e22019-09-11 12:36:07 -050028namespace openpower
29{
30namespace pels
31{
32
33namespace service_name
34{
35constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050036constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050037constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050038} // namespace service_name
39
40namespace object_path
41{
42constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
43constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050044constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis";
Vijay Lobo81b4dca2021-04-29 00:04:00 -050045constexpr auto motherBoardInv =
46 "/xyz/openbmc_project/inventory/system/chassis/motherboard";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060047constexpr auto baseInv = "/xyz/openbmc_project/inventory";
Matt Spinler4aa23a12020-02-03 15:05:09 -060048constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
49constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
Matt Spinlera7d9d962019-11-06 15:01:25 -060050constexpr auto hostState = "/xyz/openbmc_project/state/host0";
Matt Spinlerb3f51862019-12-09 13:55:10 -060051constexpr auto pldm = "/xyz/openbmc_project/pldm";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060052constexpr auto enableHostPELs =
53 "/xyz/openbmc_project/logging/send_event_logs_to_host";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050054constexpr auto vpdManager = "/com/ibm/VPD/Manager";
Matt Spinlerc8705e22019-09-11 12:36:07 -050055} // namespace object_path
56
57namespace interface
58{
59constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
60constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
61constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
Matt Spinlerf10068d2020-12-02 10:44:08 -060062constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress";
Matt Spinlerb3f51862019-12-09 13:55:10 -060063constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester";
Matt Spinler9cf3cfd2020-02-03 14:41:55 -060064constexpr auto enable = "xyz.openbmc_project.Object.Enable";
Matt Spinler4aa23a12020-02-03 15:05:09 -060065constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
66constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
67constexpr auto hostState = "xyz.openbmc_project.State.Host";
Matt Spinlerb3d488f2020-02-21 15:30:46 -060068constexpr auto invMotherboard =
69 "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
70constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
Ben Tynere32b7e72021-05-18 12:38:40 -050071constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP";
Matt Spinler60c4e792020-03-13 13:45:36 -050072constexpr auto locCode = "com.ibm.ipzvpd.Location";
Matt Spinler1ab66962020-10-29 13:21:44 -050073constexpr auto compatible =
74 "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -050075constexpr auto vpdManager = "com.ibm.VPD.Manager";
Matt Spinler34a904c2020-08-05 14:53:28 -050076constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
Matt Spinler993168d2021-04-07 16:05:03 -050077constexpr auto operationalStatus =
78 "xyz.openbmc_project.State.Decorator.OperationalStatus";
Matt Spinlerc8705e22019-09-11 12:36:07 -050079} // namespace interface
80
Matt Spinlerf10068d2020-12-02 10:44:08 -060081using namespace sdbusplus::xyz::openbmc_project::State::Boot::server;
Matt Spinlerb3d488f2020-02-21 15:30:46 -060082using sdbusplus::exception::SdBusError;
Vijay Lobo81b4dca2021-04-29 00:04:00 -050083using namespace phosphor::logging;
Matt Spinlera7d9d962019-11-06 15:01:25 -060084
Matt Spinlerc8705e22019-09-11 12:36:07 -050085DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus)
86{
Matt Spinlercad9c2b2019-12-02 15:42:01 -060087 readBMCFWVersion();
88 readServerFWVersion();
Matt Spinler677381b2020-01-23 10:04:29 -060089 readBMCFWVersionID();
Matt Spinler2a28c932020-02-03 14:23:40 -060090
Matt Spinlerf10068d2020-12-02 10:44:08 -060091 // Watch the BootProgress property
Matt Spinler2a28c932020-02-03 14:23:40 -060092 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
Matt Spinlerf10068d2020-12-02 10:44:08 -060093 bus, object_path::hostState, interface::bootProgress, "BootProgress",
94 *this, [this](const auto& value) {
95 auto status = Progress::convertProgressStagesFromString(
96 std::get<std::string>(value));
Matt Spinler2a28c932020-02-03 14:23:40 -060097
Matt Spinlerf10068d2020-12-02 10:44:08 -060098 if ((status == Progress::ProgressStages::SystemInitComplete) ||
99 (status == Progress::ProgressStages::OSStart) ||
100 (status == Progress::ProgressStages::OSRunning))
Matt Spinler2a28c932020-02-03 14:23:40 -0600101 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600102 setHostUp(true);
Matt Spinler2a28c932020-02-03 14:23:40 -0600103 }
104 else
105 {
Matt Spinler4aa23a12020-02-03 15:05:09 -0600106 setHostUp(false);
Matt Spinler2a28c932020-02-03 14:23:40 -0600107 }
108 }));
Matt Spinler9cf3cfd2020-02-03 14:41:55 -0600109
110 // Watch the host PEL enable property
111 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
112 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
113 [this](const auto& value) {
114 this->_sendPELsToHost = std::get<bool>(value);
115 }));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600116
117 // Watch the BMCState property
118 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
119 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
120 *this, [this](const auto& value) {
121 this->_bmcState = std::get<std::string>(value);
122 }));
123
124 // Watch the chassis current and requested power state properties
125 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
126 bus, object_path::chassisState, interface::chassisState, *this,
127 [this](const auto& properties) {
128 auto state = properties.find("CurrentPowerState");
129 if (state != properties.end())
130 {
131 this->_chassisState = std::get<std::string>(state->second);
132 }
133
134 auto trans = properties.find("RequestedPowerTransition");
135 if (trans != properties.end())
136 {
137 this->_chassisTransition = std::get<std::string>(trans->second);
138 }
139 }));
140
141 // Watch the CurrentHostState property
142 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
143 bus, object_path::hostState, interface::hostState, "CurrentHostState",
144 *this, [this](const auto& value) {
145 this->_hostState = std::get<std::string>(value);
146 }));
Matt Spinlerc8705e22019-09-11 12:36:07 -0500147}
148
Matt Spinler2a28c932020-02-03 14:23:40 -0600149DBusPropertyMap
150 DataInterface::getAllProperties(const std::string& service,
151 const std::string& objectPath,
152 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500153{
154 DBusPropertyMap properties;
155
156 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
157 interface::dbusProperty, "GetAll");
158 method.append(interface);
159 auto reply = _bus.call(method);
160
161 reply.read(properties);
162
163 return properties;
164}
165
Matt Spinlera7d9d962019-11-06 15:01:25 -0600166void DataInterface::getProperty(const std::string& service,
167 const std::string& objectPath,
168 const std::string& interface,
Matt Spinler2a28c932020-02-03 14:23:40 -0600169 const std::string& property,
170 DBusValue& value) const
Matt Spinlera7d9d962019-11-06 15:01:25 -0600171{
172
173 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
174 interface::dbusProperty, "Get");
175 method.append(interface, property);
176 auto reply = _bus.call(method);
177
178 reply.read(value);
179}
180
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600181DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
182{
183
184 auto method = _bus.new_method_call(
185 service_name::objectMapper, object_path::objectMapper,
186 interface::objectMapper, "GetSubTreePaths");
187
188 method.append(std::string{"/"}, 0, interfaces);
189
190 auto reply = _bus.call(method);
191
192 DBusPathList paths;
193 reply.read(paths);
194
195 return paths;
196}
197
Matt Spinlerc8705e22019-09-11 12:36:07 -0500198DBusService DataInterface::getService(const std::string& objectPath,
Matt Spinlerb3f51862019-12-09 13:55:10 -0600199 const std::string& interface) const
Matt Spinlerc8705e22019-09-11 12:36:07 -0500200{
201 auto method = _bus.new_method_call(service_name::objectMapper,
202 object_path::objectMapper,
203 interface::objectMapper, "GetObject");
204
205 method.append(objectPath, std::vector<std::string>({interface}));
206
207 auto reply = _bus.call(method);
208
209 std::map<DBusService, DBusInterfaceList> response;
210 reply.read(response);
211
212 if (!response.empty())
213 {
214 return response.begin()->first;
215 }
216
217 return std::string{};
218}
Matt Spinlera7d9d962019-11-06 15:01:25 -0600219
Matt Spinler677381b2020-01-23 10:04:29 -0600220void DataInterface::readBMCFWVersion()
221{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500222 _bmcFWVersion =
223 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600224}
225
226void DataInterface::readServerFWVersion()
227{
Sumit Kumarcad16202021-05-13 04:06:15 -0500228 auto value =
229 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
230 if ((value != "") && (value.find_last_of(')') != std::string::npos))
231 {
232 std::size_t pos = value.find_first_of('(') + 1;
233 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
234 }
Matt Spinlercad9c2b2019-12-02 15:42:01 -0600235}
236
Matt Spinler677381b2020-01-23 10:04:29 -0600237void DataInterface::readBMCFWVersionID()
238{
Matt Spinlerf61f2922020-06-23 11:32:49 -0500239 _bmcFWVersionID =
240 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
Matt Spinler677381b2020-01-23 10:04:29 -0600241}
242
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500243std::string DataInterface::getMachineTypeModel() const
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600244{
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500245 std::string model;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600246 try
247 {
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600248
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500249 auto service = getService(object_path::systemInv, interface::invAsset);
250 if (!service.empty())
251 {
252 DBusValue value;
253 getProperty(service, object_path::systemInv, interface::invAsset,
254 "Model", value);
255
256 model = std::get<std::string>(value);
257 }
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600258 }
259 catch (const std::exception& e)
260 {
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500261 log<level::WARNING>(fmt::format("Failed reading Model property from "
262 "Interface: {} exception: {}",
263 interface::invAsset, e.what())
264 .c_str());
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600265 }
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500266
267 return model;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600268}
269
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500270std::string DataInterface::getMachineSerialNumber() const
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600271{
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500272 std::string sn;
273 try
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600274 {
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500275
276 auto service = getService(object_path::systemInv, interface::invAsset);
277 if (!service.empty())
278 {
279 DBusValue value;
280 getProperty(service, object_path::systemInv, interface::invAsset,
281 "SerialNumber", value);
282
283 sn = std::get<std::string>(value);
284 }
285 }
286 catch (const std::exception& e)
287 {
288 log<level::WARNING>(
289 fmt::format("Failed reading SerialNumber property from "
290 "Interface: {} exception: {}",
291 interface::invAsset, e.what())
292 .c_str());
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600293 }
294
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500295 return sn;
296}
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600297
Vijay Lobo81b4dca2021-04-29 00:04:00 -0500298std::string DataInterface::getMotherboardCCIN() const
299{
300 std::string ccin;
301
302 try
303 {
304 auto service =
305 getService(object_path::motherBoardInv, interface::viniRecordVPD);
306 if (!service.empty())
307 {
308 DBusValue value;
309 getProperty(service, object_path::motherBoardInv,
310 interface::viniRecordVPD, "CC", value);
311
312 auto cc = std::get<std::vector<uint8_t>>(value);
313 ccin = std::string{cc.begin(), cc.end()};
314 }
315 }
316 catch (const std::exception& e)
317 {
318 log<level::WARNING>(
319 fmt::format("Failed reading Motherboard CCIN property from "
320 "Interface: {} exception: {}",
321 interface::viniRecordVPD, e.what())
322 .c_str());
323 }
324
325 return ccin;
Matt Spinlerb3d488f2020-02-21 15:30:46 -0600326}
327
Ben Tynere32b7e72021-05-18 12:38:40 -0500328std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
329{
330 std::vector<uint8_t> systemIM;
331
332 try
333 {
334 auto service =
335 getService(object_path::motherBoardInv, interface::vsbpRecordVPD);
336 if (!service.empty())
337 {
338 DBusValue value;
339 getProperty(service, object_path::motherBoardInv,
340 interface::vsbpRecordVPD, "IM", value);
341
342 systemIM = std::get<std::vector<uint8_t>>(value);
343 }
344 }
345 catch (const std::exception& e)
346 {
347 log<level::WARNING>(
348 fmt::format("Failed reading System IM property from "
349 "Interface: {} exception: {}",
350 interface::vsbpRecordVPD, e.what())
351 .c_str());
352 }
353
354 return systemIM;
355}
356
Matt Spinler60c4e792020-03-13 13:45:36 -0500357void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
Matt Spinler60c4e792020-03-13 13:45:36 -0500358 std::string& fruPartNumber,
359 std::string& ccin,
360 std::string& serialNumber) const
361{
362 // For now, attempt to get all of the properties directly on the path
363 // passed in. In the future, may need to make use of an algorithm
364 // to figure out which inventory objects actually hold these
365 // interfaces in the case of non FRUs, or possibly another service
366 // will provide this info. Any missing interfaces will result
367 // in exceptions being thrown.
368
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500369 auto service = getService(inventoryPath, interface::viniRecordVPD);
Matt Spinler60c4e792020-03-13 13:45:36 -0500370
371 auto properties =
372 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
373
374 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
375 fruPartNumber = std::string{value.begin(), value.end()};
376
377 value = std::get<std::vector<uint8_t>>(properties["CC"]);
378 ccin = std::string{value.begin(), value.end()};
379
380 value = std::get<std::vector<uint8_t>>(properties["SN"]);
381 serialNumber = std::string{value.begin(), value.end()};
382}
383
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500384std::string
385 DataInterface::getLocationCode(const std::string& inventoryPath) const
386{
387 auto service = getService(inventoryPath, interface::locCode);
388
389 DBusValue locCode;
390 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
391 locCode);
392
393 return std::get<std::string>(locCode);
394}
395
Matt Spinler5fb24c12020-06-04 11:21:33 -0500396std::string
397 DataInterface::addLocationCodePrefix(const std::string& locationCode)
398{
399 static const std::string locationCodePrefix{"Ufcs-"};
400
Matt Spinler0e4d72e2020-08-05 12:36:53 -0500401 // Technically there are 2 location code prefixes, Ufcs and Umts, so
402 // if it already starts with a U then don't need to do anything.
403 if (locationCode.front() != 'U')
Matt Spinler5fb24c12020-06-04 11:21:33 -0500404 {
405 return locationCodePrefix + locationCode;
406 }
407
408 return locationCode;
409}
410
411std::string DataInterface::expandLocationCode(const std::string& locationCode,
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500412 uint16_t /*node*/) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500413{
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500414 auto method =
415 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
416 interface::vpdManager, "GetExpandedLocationCode");
417
418 method.append(addLocationCodePrefix(locationCode),
419 static_cast<uint16_t>(0));
420
421 auto reply = _bus.call(method);
422
423 std::string expandedLocationCode;
424 reply.read(expandedLocationCode);
425
426 return expandedLocationCode;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500427}
428
Matt Spinler2f9225a2020-08-05 12:58:49 -0500429std::string
430 DataInterface::getInventoryFromLocCode(const std::string& locationCode,
431 uint16_t node, bool expanded) const
Matt Spinler5fb24c12020-06-04 11:21:33 -0500432{
Matt Spinler2f9225a2020-08-05 12:58:49 -0500433 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
434 : "GetFRUsByUnexpandedLocationCode";
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500435
Matt Spinler2f9225a2020-08-05 12:58:49 -0500436 auto method =
437 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
438 interface::vpdManager, methodName.c_str());
439
440 if (expanded)
441 {
442 method.append(locationCode);
443 }
444 else
445 {
446 method.append(addLocationCodePrefix(locationCode), node);
447 }
Matt Spinlerfcf9a3f2020-07-28 13:21:07 -0500448
449 auto reply = _bus.call(method);
450
451 std::vector<sdbusplus::message::object_path> entries;
452 reply.read(entries);
453
454 // Get the shortest entry from the paths received, as this
455 // would be the path furthest up the inventory hierarchy so
456 // would be the parent FRU. There is guaranteed to at least
457 // be one entry if the call didn't fail.
458 std::string shortest{entries[0]};
459
460 std::for_each(entries.begin(), entries.end(),
461 [&shortest](const auto& path) {
462 if (path.str.size() < shortest.size())
463 {
464 shortest = path;
465 }
466 });
467
468 return shortest;
Matt Spinler5fb24c12020-06-04 11:21:33 -0500469}
470
Matt Spinler34a904c2020-08-05 14:53:28 -0500471void DataInterface::assertLEDGroup(const std::string& ledGroup,
472 bool value) const
473{
474 DBusValue variant = value;
475 auto method =
476 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
477 interface::dbusProperty, "Set");
478 method.append(interface::ledGroup, "Asserted", variant);
479 _bus.call(method);
480}
481
Matt Spinler993168d2021-04-07 16:05:03 -0500482void DataInterface::setFunctional(const std::string& objectPath,
483 bool value) const
484{
485 DBusValue variant = value;
486 auto service = getService(objectPath, interface::operationalStatus);
487
488 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
489 interface::dbusProperty, "Set");
490
491 method.append(interface::operationalStatus, "Functional", variant);
492 _bus.call(method);
493}
494
Matt Spinler1ab66962020-10-29 13:21:44 -0500495std::vector<std::string> DataInterface::getSystemNames() const
496{
497 DBusSubTree subtree;
498 DBusValue names;
499
500 auto method = _bus.new_method_call(service_name::objectMapper,
501 object_path::objectMapper,
502 interface::objectMapper, "GetSubTree");
503 method.append(std::string{"/"}, 0,
504 std::vector<std::string>{interface::compatible});
505 auto reply = _bus.call(method);
506
507 reply.read(subtree);
508 if (subtree.empty())
509 {
510 throw std::runtime_error("Compatible interface not on D-Bus");
511 }
512
513 const auto& object = *(subtree.begin());
514 const auto& path = object.first;
515 const auto& service = object.second.begin()->first;
516
517 getProperty(service, path, interface::compatible, "Names", names);
518
519 return std::get<std::vector<std::string>>(names);
520}
521
Matt Spinlerc8705e22019-09-11 12:36:07 -0500522} // namespace pels
523} // namespace openpower