blob: 2a5cc26650cb53b972f6835a4816b693836dd0be [file] [log] [blame]
Shawn McCarney6a957f62024-01-10 16:15:19 -06001/**
2 * Copyright © 2024 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 */
16
17#include "config_file_parser.hpp"
18
19#include "config_file_parser_error.hpp"
Shawn McCarney38f85002025-10-31 17:59:36 -050020#include "json_parser_utils.hpp"
Shawn McCarney4840b252025-11-06 16:06:40 -060021#include "ucd90160_device.hpp"
22#include "ucd90320_device.hpp"
Shawn McCarney6a957f62024-01-10 16:15:19 -060023
Shawn McCarney38f85002025-10-31 17:59:36 -050024#include <cstdint>
Shawn McCarney6a957f62024-01-10 16:15:19 -060025#include <exception>
26#include <fstream>
27#include <optional>
Shawn McCarney38f85002025-10-31 17:59:36 -050028#include <stdexcept>
Shawn McCarney6a957f62024-01-10 16:15:19 -060029
Shawn McCarney38f85002025-10-31 17:59:36 -050030using namespace phosphor::power::json_parser_utils;
31using ConfigFileParserError = phosphor::power::util::ConfigFileParserError;
Shawn McCarneye9144ab2024-05-22 12:34:18 -050032namespace fs = std::filesystem;
Shawn McCarney6a957f62024-01-10 16:15:19 -060033
34namespace phosphor::power::sequencer::config_file_parser
35{
36
Shawn McCarneye9144ab2024-05-22 12:34:18 -050037const std::filesystem::path standardConfigFileDirectory{
38 "/usr/share/phosphor-power-sequencer"};
39
Patrick Williams92261f82025-02-01 08:22:34 -050040std::filesystem::path find(
41 const std::vector<std::string>& compatibleSystemTypes,
42 const std::filesystem::path& configFileDir)
Shawn McCarneye9144ab2024-05-22 12:34:18 -050043{
44 fs::path pathName, possiblePath;
45 std::string fileName;
46
47 for (const std::string& systemType : compatibleSystemTypes)
48 {
49 // Look for file name that is entire system type + ".json"
50 // Example: com.acme.Hardware.Chassis.Model.MegaServer.json
51 fileName = systemType + ".json";
52 possiblePath = configFileDir / fileName;
53 if (fs::is_regular_file(possiblePath))
54 {
55 pathName = possiblePath;
56 break;
57 }
58
59 // Look for file name that is last node of system type + ".json"
60 // Example: MegaServer.json
61 std::string::size_type pos = systemType.rfind('.');
62 if ((pos != std::string::npos) && ((systemType.size() - pos) > 1))
63 {
64 fileName = systemType.substr(pos + 1) + ".json";
65 possiblePath = configFileDir / fileName;
66 if (fs::is_regular_file(possiblePath))
67 {
68 pathName = possiblePath;
69 break;
70 }
71 }
72 }
73
74 return pathName;
75}
76
Shawn McCarneyf1d20602025-11-12 17:27:48 -060077std::vector<std::unique_ptr<Chassis>> parse(
78 const std::filesystem::path& pathName, Services& services)
Shawn McCarney6a957f62024-01-10 16:15:19 -060079{
80 try
81 {
82 // Use standard JSON parser to create tree of JSON elements
83 std::ifstream file{pathName};
84 json rootElement = json::parse(file);
85
86 // Parse tree of JSON elements and return corresponding C++ objects
Shawn McCarneyf1d20602025-11-12 17:27:48 -060087 return internal::parseRoot(rootElement, services);
Shawn McCarney6a957f62024-01-10 16:15:19 -060088 }
89 catch (const std::exception& e)
90 {
91 throw ConfigFileParserError{pathName, e.what()};
92 }
93}
94
95namespace internal
96{
97
Shawn McCarneye02aa3c2025-11-10 12:25:54 -060098std::unique_ptr<Chassis> parseChassis(
Shawn McCarney1cd07b82025-11-12 13:29:26 -060099 const json& element,
100 const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600101 Services& services)
102{
103 verifyIsObject(element);
104
105 // If chassis object is not using a template, parse properties normally
106 if (!element.contains("template_id"))
107 {
108 bool isChassisTemplate{false};
109 return parseChassisProperties(element, isChassisTemplate, NO_VARIABLES,
110 services);
111 }
112
113 // Parse chassis object that is using a template
114 unsigned int propertyCount{0};
115
116 // Optional comments property; value not stored
117 if (element.contains("comments"))
118 {
119 ++propertyCount;
120 }
121
122 // Required template_id property
123 const json& templateIDElement = getRequiredProperty(element, "template_id");
124 std::string templateID = parseString(templateIDElement);
125 ++propertyCount;
126
127 // Required template_variable_values property
128 const json& variablesElement =
129 getRequiredProperty(element, "template_variable_values");
130 std::map<std::string, std::string> variables =
131 parseVariables(variablesElement);
132 ++propertyCount;
133
134 // Verify no invalid properties exist
135 verifyPropertyCount(element, propertyCount);
136
137 // Get reference to chassis template JSON
138 auto it = chassisTemplates.find(templateID);
139 if (it == chassisTemplates.end())
140 {
141 throw std::invalid_argument{
142 "Invalid chassis template id: " + templateID};
143 }
144 const json& templateElement = it->second.get();
145
146 // Parse properties in template using variable values for this chassis
147 bool isChassisTemplate{true};
148 return parseChassisProperties(templateElement, isChassisTemplate, variables,
149 services);
150}
151
152std::vector<std::unique_ptr<Chassis>> parseChassisArray(
Shawn McCarney1cd07b82025-11-12 13:29:26 -0600153 const json& element,
154 const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600155 Services& services)
156{
157 verifyIsArray(element);
158 std::vector<std::unique_ptr<Chassis>> chassis;
159 for (auto& chassisElement : element)
160 {
161 chassis.emplace_back(
162 parseChassis(chassisElement, chassisTemplates, services));
163 }
164 return chassis;
165}
166
167std::unique_ptr<Chassis> parseChassisProperties(
168 const json& element, bool isChassisTemplate,
169 const std::map<std::string, std::string>& variables, Services& services)
170
171{
172 verifyIsObject(element);
173 unsigned int propertyCount{0};
174
175 // Optional comments property; value not stored
176 if (element.contains("comments"))
177 {
178 ++propertyCount;
179 }
180
181 // Required id property if this is a chassis template
182 // Don't parse again; this was already parsed by parseChassisTemplate()
183 if (isChassisTemplate)
184 {
185 getRequiredProperty(element, "id");
186 ++propertyCount;
187 }
188
189 // Required number property
190 const json& numberElement = getRequiredProperty(element, "number");
191 unsigned int number = parseUnsignedInteger(numberElement, variables);
192 if (number < 1)
193 {
194 throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
195 }
196 ++propertyCount;
197
198 // Required inventory_path property
199 const json& inventoryPathElement =
200 getRequiredProperty(element, "inventory_path");
201 std::string inventoryPath =
202 parseString(inventoryPathElement, false, variables);
203 ++propertyCount;
204
205 // Required power_sequencers property
206 const json& powerSequencersElement =
207 getRequiredProperty(element, "power_sequencers");
208 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers =
209 parsePowerSequencerArray(powerSequencersElement, variables, services);
210 ++propertyCount;
211
212 // Verify no invalid properties exist
213 verifyPropertyCount(element, propertyCount);
214
215 return std::make_unique<Chassis>(number, inventoryPath,
216 std::move(powerSequencers));
217}
218
Shawn McCarneyfb7e0932025-11-10 10:23:05 -0600219std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
220 const json& element)
221{
222 verifyIsObject(element);
223 unsigned int propertyCount{0};
224
225 // Optional comments property; value not stored
226 if (element.contains("comments"))
227 {
228 ++propertyCount;
229 }
230
231 // Required id property
232 const json& idElement = getRequiredProperty(element, "id");
233 std::string id = parseString(idElement);
234 ++propertyCount;
235
236 // Required number property
237 // Just verify it exists; cannot be parsed without variable values
238 getRequiredProperty(element, "number");
239 ++propertyCount;
240
241 // Required inventory_path property
242 // Just verify it exists; cannot be parsed without variable values
243 getRequiredProperty(element, "inventory_path");
244 ++propertyCount;
245
246 // Required power_sequencers property
247 // Just verify it exists; cannot be parsed without variable values
248 getRequiredProperty(element, "power_sequencers");
249 ++propertyCount;
250
251 // Verify no invalid properties exist
252 verifyPropertyCount(element, propertyCount);
253
254 return {id, JSONRefWrapper{element}};
255}
256
Shawn McCarney1cd07b82025-11-12 13:29:26 -0600257std::map<std::string, JSONRefWrapper> parseChassisTemplateArray(
258 const json& element)
259{
260 verifyIsArray(element);
261 std::map<std::string, JSONRefWrapper> chassisTemplates;
262 for (auto& chassisTemplateElement : element)
263 {
264 chassisTemplates.emplace(parseChassisTemplate(chassisTemplateElement));
265 }
266 return chassisTemplates;
267}
268
Shawn McCarney984be692025-11-18 16:38:29 -0600269PgoodGPIO parseGPIO(const json& element,
270 const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600271{
272 verifyIsObject(element);
273 unsigned int propertyCount{0};
274
275 // Required line property
276 const json& lineElement = getRequiredProperty(element, "line");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600277 unsigned int line = parseUnsignedInteger(lineElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600278 ++propertyCount;
279
280 // Optional active_low property
281 bool activeLow{false};
282 auto activeLowIt = element.find("active_low");
283 if (activeLowIt != element.end())
284 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600285 activeLow = parseBoolean(*activeLowIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600286 ++propertyCount;
287 }
288
289 // Verify no invalid properties exist
290 verifyPropertyCount(element, propertyCount);
291
Shawn McCarney984be692025-11-18 16:38:29 -0600292 return PgoodGPIO(line, activeLow);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600293}
294
Shawn McCarneye140ed92025-11-06 11:20:38 -0600295std::tuple<uint8_t, uint16_t> parseI2CInterface(
296 const nlohmann::json& element,
297 const std::map<std::string, std::string>& variables)
298{
299 verifyIsObject(element);
300 unsigned int propertyCount{0};
301
302 // Required bus property
303 const json& busElement = getRequiredProperty(element, "bus");
304 uint8_t bus = parseUint8(busElement, variables);
305 ++propertyCount;
306
307 // Required address property
308 const json& addressElement = getRequiredProperty(element, "address");
309 uint16_t address = parseHexByte(addressElement, variables);
310 ++propertyCount;
311
312 // Verify no invalid properties exist
313 verifyPropertyCount(element, propertyCount);
314
315 return {bus, address};
316}
317
Shawn McCarney4840b252025-11-06 16:06:40 -0600318std::unique_ptr<PowerSequencerDevice> parsePowerSequencer(
319 const nlohmann::json& element,
320 const std::map<std::string, std::string>& variables, Services& services)
321{
322 verifyIsObject(element);
323 unsigned int propertyCount{0};
324
325 // Optional comments property; value not stored
326 if (element.contains("comments"))
327 {
328 ++propertyCount;
329 }
330
331 // Required type property
332 const json& typeElement = getRequiredProperty(element, "type");
333 std::string type = parseString(typeElement, false, variables);
334 ++propertyCount;
335
336 // Required i2c_interface property
337 const json& i2cInterfaceElement =
338 getRequiredProperty(element, "i2c_interface");
339 auto [bus, address] = parseI2CInterface(i2cInterfaceElement, variables);
340 ++propertyCount;
341
342 // Required power_control_gpio_name property
343 const json& powerControlGPIONameElement =
344 getRequiredProperty(element, "power_control_gpio_name");
345 std::string powerControlGPIOName =
346 parseString(powerControlGPIONameElement, false, variables);
347 ++propertyCount;
348
349 // Required power_good_gpio_name property
350 const json& powerGoodGPIONameElement =
351 getRequiredProperty(element, "power_good_gpio_name");
352 std::string powerGoodGPIOName =
353 parseString(powerGoodGPIONameElement, false, variables);
354 ++propertyCount;
355
356 // Required rails property
357 const json& railsElement = getRequiredProperty(element, "rails");
358 std::vector<std::unique_ptr<Rail>> rails =
359 parseRailArray(railsElement, variables);
360 ++propertyCount;
361
362 // Verify no invalid properties exist
363 verifyPropertyCount(element, propertyCount);
364
365 if (type == UCD90160Device::deviceName)
366 {
367 return std::make_unique<UCD90160Device>(
368 bus, address, powerControlGPIOName, powerGoodGPIOName,
369 std::move(rails), services);
370 }
371 else if (type == UCD90320Device::deviceName)
372 {
373 return std::make_unique<UCD90320Device>(
374 bus, address, powerControlGPIOName, powerGoodGPIOName,
375 std::move(rails), services);
376 }
377 throw std::invalid_argument{"Invalid power sequencer type: " + type};
378}
379
380std::vector<std::unique_ptr<PowerSequencerDevice>> parsePowerSequencerArray(
381 const nlohmann::json& element,
382 const std::map<std::string, std::string>& variables, Services& services)
383{
384 verifyIsArray(element);
385 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers;
386 for (auto& powerSequencerElement : element)
387 {
388 powerSequencers.emplace_back(
389 parsePowerSequencer(powerSequencerElement, variables, services));
390 }
391 return powerSequencers;
392}
393
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600394std::unique_ptr<Rail> parseRail(
395 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600396{
397 verifyIsObject(element);
398 unsigned int propertyCount{0};
399
400 // Required name property
401 const json& nameElement = getRequiredProperty(element, "name");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600402 std::string name = parseString(nameElement, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600403 ++propertyCount;
404
405 // Optional presence property
406 std::optional<std::string> presence{};
407 auto presenceIt = element.find("presence");
408 if (presenceIt != element.end())
409 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600410 presence = parseString(*presenceIt, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600411 ++propertyCount;
412 }
413
414 // Optional page property
415 std::optional<uint8_t> page{};
416 auto pageIt = element.find("page");
417 if (pageIt != element.end())
418 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600419 page = parseUint8(*pageIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600420 ++propertyCount;
421 }
422
Shawn McCarney16e493a2024-01-29 14:20:32 -0600423 // Optional is_power_supply_rail property
424 bool isPowerSupplyRail{false};
425 auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
426 if (isPowerSupplyRailIt != element.end())
427 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600428 isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt, variables);
Shawn McCarney16e493a2024-01-29 14:20:32 -0600429 ++propertyCount;
430 }
431
Shawn McCarney6a957f62024-01-10 16:15:19 -0600432 // Optional check_status_vout property
433 bool checkStatusVout{false};
434 auto checkStatusVoutIt = element.find("check_status_vout");
435 if (checkStatusVoutIt != element.end())
436 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600437 checkStatusVout = parseBoolean(*checkStatusVoutIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600438 ++propertyCount;
439 }
440
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600441 // Optional compare_voltage_to_limit property
442 bool compareVoltageToLimit{false};
443 auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit");
444 if (compareVoltageToLimitIt != element.end())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600445 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600446 compareVoltageToLimit =
447 parseBoolean(*compareVoltageToLimitIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600448 ++propertyCount;
449 }
450
451 // Optional gpio property
Shawn McCarney984be692025-11-18 16:38:29 -0600452 std::optional<PgoodGPIO> gpio{};
Shawn McCarney6a957f62024-01-10 16:15:19 -0600453 auto gpioIt = element.find("gpio");
454 if (gpioIt != element.end())
455 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600456 gpio = parseGPIO(*gpioIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600457 ++propertyCount;
458 }
459
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600460 // If check_status_vout or compare_voltage_to_limit property is true, the
461 // page property is required; verify page was specified
462 if ((checkStatusVout || compareVoltageToLimit) && !page.has_value())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600463 {
464 throw std::invalid_argument{"Required property missing: page"};
465 }
466
467 // Verify no invalid properties exist
468 verifyPropertyCount(element, propertyCount);
469
Shawn McCarney16e493a2024-01-29 14:20:32 -0600470 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600471 checkStatusVout, compareVoltageToLimit, gpio);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600472}
473
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600474std::vector<std::unique_ptr<Rail>> parseRailArray(
475 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600476{
477 verifyIsArray(element);
478 std::vector<std::unique_ptr<Rail>> rails;
479 for (auto& railElement : element)
480 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600481 rails.emplace_back(parseRail(railElement, variables));
Shawn McCarney6a957f62024-01-10 16:15:19 -0600482 }
483 return rails;
484}
485
Shawn McCarneyf1d20602025-11-12 17:27:48 -0600486std::vector<std::unique_ptr<Chassis>> parseRoot(const json& element,
487 Services& services)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600488{
489 verifyIsObject(element);
490 unsigned int propertyCount{0};
491
Shawn McCarneyf1d20602025-11-12 17:27:48 -0600492 // Optional comments property; value not stored
493 if (element.contains("comments"))
494 {
495 ++propertyCount;
496 }
497
498 // Optional chassis_templates property
499 std::map<std::string, JSONRefWrapper> chassisTemplates{};
500 auto chassisTemplatesIt = element.find("chassis_templates");
501 if (chassisTemplatesIt != element.end())
502 {
503 chassisTemplates = parseChassisTemplateArray(*chassisTemplatesIt);
504 ++propertyCount;
505 }
506
507 // Required chassis property
508 const json& chassisElement = getRequiredProperty(element, "chassis");
509 std::vector<std::unique_ptr<Chassis>> chassis =
510 parseChassisArray(chassisElement, chassisTemplates, services);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600511 ++propertyCount;
512
513 // Verify no invalid properties exist
514 verifyPropertyCount(element, propertyCount);
515
Shawn McCarneyf1d20602025-11-12 17:27:48 -0600516 return chassis;
Shawn McCarney6a957f62024-01-10 16:15:19 -0600517}
518
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600519std::map<std::string, std::string> parseVariables(const json& element)
520{
521 verifyIsObject(element);
522
523 std::map<std::string, std::string> variables;
524 std::string name, value;
525 for (const auto& [nameElement, valueElement] : element.items())
526 {
527 name = parseString(nameElement);
528 value = parseString(valueElement);
529 variables.emplace(name, value);
530 }
531 return variables;
532}
533
Shawn McCarney6a957f62024-01-10 16:15:19 -0600534} // namespace internal
535
536} // namespace phosphor::power::sequencer::config_file_parser