blob: 18fe32e3855a10a9ed22450e155205743fc44e08 [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 McCarney6a957f62024-01-10 16:15:19 -060077std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName)
78{
79 try
80 {
81 // Use standard JSON parser to create tree of JSON elements
82 std::ifstream file{pathName};
83 json rootElement = json::parse(file);
84
85 // Parse tree of JSON elements and return corresponding C++ objects
86 return internal::parseRoot(rootElement);
87 }
88 catch (const std::exception& e)
89 {
90 throw ConfigFileParserError{pathName, e.what()};
91 }
92}
93
94namespace internal
95{
96
Shawn McCarneye02aa3c2025-11-10 12:25:54 -060097std::unique_ptr<Chassis> parseChassis(
98 const json& element, std::map<std::string, JSONRefWrapper> chassisTemplates,
99 Services& services)
100{
101 verifyIsObject(element);
102
103 // If chassis object is not using a template, parse properties normally
104 if (!element.contains("template_id"))
105 {
106 bool isChassisTemplate{false};
107 return parseChassisProperties(element, isChassisTemplate, NO_VARIABLES,
108 services);
109 }
110
111 // Parse chassis object that is using a template
112 unsigned int propertyCount{0};
113
114 // Optional comments property; value not stored
115 if (element.contains("comments"))
116 {
117 ++propertyCount;
118 }
119
120 // Required template_id property
121 const json& templateIDElement = getRequiredProperty(element, "template_id");
122 std::string templateID = parseString(templateIDElement);
123 ++propertyCount;
124
125 // Required template_variable_values property
126 const json& variablesElement =
127 getRequiredProperty(element, "template_variable_values");
128 std::map<std::string, std::string> variables =
129 parseVariables(variablesElement);
130 ++propertyCount;
131
132 // Verify no invalid properties exist
133 verifyPropertyCount(element, propertyCount);
134
135 // Get reference to chassis template JSON
136 auto it = chassisTemplates.find(templateID);
137 if (it == chassisTemplates.end())
138 {
139 throw std::invalid_argument{
140 "Invalid chassis template id: " + templateID};
141 }
142 const json& templateElement = it->second.get();
143
144 // Parse properties in template using variable values for this chassis
145 bool isChassisTemplate{true};
146 return parseChassisProperties(templateElement, isChassisTemplate, variables,
147 services);
148}
149
150std::vector<std::unique_ptr<Chassis>> parseChassisArray(
151 const json& element, std::map<std::string, JSONRefWrapper> chassisTemplates,
152 Services& services)
153{
154 verifyIsArray(element);
155 std::vector<std::unique_ptr<Chassis>> chassis;
156 for (auto& chassisElement : element)
157 {
158 chassis.emplace_back(
159 parseChassis(chassisElement, chassisTemplates, services));
160 }
161 return chassis;
162}
163
164std::unique_ptr<Chassis> parseChassisProperties(
165 const json& element, bool isChassisTemplate,
166 const std::map<std::string, std::string>& variables, Services& services)
167
168{
169 verifyIsObject(element);
170 unsigned int propertyCount{0};
171
172 // Optional comments property; value not stored
173 if (element.contains("comments"))
174 {
175 ++propertyCount;
176 }
177
178 // Required id property if this is a chassis template
179 // Don't parse again; this was already parsed by parseChassisTemplate()
180 if (isChassisTemplate)
181 {
182 getRequiredProperty(element, "id");
183 ++propertyCount;
184 }
185
186 // Required number property
187 const json& numberElement = getRequiredProperty(element, "number");
188 unsigned int number = parseUnsignedInteger(numberElement, variables);
189 if (number < 1)
190 {
191 throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
192 }
193 ++propertyCount;
194
195 // Required inventory_path property
196 const json& inventoryPathElement =
197 getRequiredProperty(element, "inventory_path");
198 std::string inventoryPath =
199 parseString(inventoryPathElement, false, variables);
200 ++propertyCount;
201
202 // Required power_sequencers property
203 const json& powerSequencersElement =
204 getRequiredProperty(element, "power_sequencers");
205 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers =
206 parsePowerSequencerArray(powerSequencersElement, variables, services);
207 ++propertyCount;
208
209 // Verify no invalid properties exist
210 verifyPropertyCount(element, propertyCount);
211
212 return std::make_unique<Chassis>(number, inventoryPath,
213 std::move(powerSequencers));
214}
215
Shawn McCarneyfb7e0932025-11-10 10:23:05 -0600216std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
217 const json& element)
218{
219 verifyIsObject(element);
220 unsigned int propertyCount{0};
221
222 // Optional comments property; value not stored
223 if (element.contains("comments"))
224 {
225 ++propertyCount;
226 }
227
228 // Required id property
229 const json& idElement = getRequiredProperty(element, "id");
230 std::string id = parseString(idElement);
231 ++propertyCount;
232
233 // Required number property
234 // Just verify it exists; cannot be parsed without variable values
235 getRequiredProperty(element, "number");
236 ++propertyCount;
237
238 // Required inventory_path property
239 // Just verify it exists; cannot be parsed without variable values
240 getRequiredProperty(element, "inventory_path");
241 ++propertyCount;
242
243 // Required power_sequencers property
244 // Just verify it exists; cannot be parsed without variable values
245 getRequiredProperty(element, "power_sequencers");
246 ++propertyCount;
247
248 // Verify no invalid properties exist
249 verifyPropertyCount(element, propertyCount);
250
251 return {id, JSONRefWrapper{element}};
252}
253
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600254GPIO parseGPIO(const json& element,
255 const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600256{
257 verifyIsObject(element);
258 unsigned int propertyCount{0};
259
260 // Required line property
261 const json& lineElement = getRequiredProperty(element, "line");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600262 unsigned int line = parseUnsignedInteger(lineElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600263 ++propertyCount;
264
265 // Optional active_low property
266 bool activeLow{false};
267 auto activeLowIt = element.find("active_low");
268 if (activeLowIt != element.end())
269 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600270 activeLow = parseBoolean(*activeLowIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600271 ++propertyCount;
272 }
273
274 // Verify no invalid properties exist
275 verifyPropertyCount(element, propertyCount);
276
277 return GPIO(line, activeLow);
278}
279
Shawn McCarneye140ed92025-11-06 11:20:38 -0600280std::tuple<uint8_t, uint16_t> parseI2CInterface(
281 const nlohmann::json& element,
282 const std::map<std::string, std::string>& variables)
283{
284 verifyIsObject(element);
285 unsigned int propertyCount{0};
286
287 // Required bus property
288 const json& busElement = getRequiredProperty(element, "bus");
289 uint8_t bus = parseUint8(busElement, variables);
290 ++propertyCount;
291
292 // Required address property
293 const json& addressElement = getRequiredProperty(element, "address");
294 uint16_t address = parseHexByte(addressElement, variables);
295 ++propertyCount;
296
297 // Verify no invalid properties exist
298 verifyPropertyCount(element, propertyCount);
299
300 return {bus, address};
301}
302
Shawn McCarney4840b252025-11-06 16:06:40 -0600303std::unique_ptr<PowerSequencerDevice> parsePowerSequencer(
304 const nlohmann::json& element,
305 const std::map<std::string, std::string>& variables, Services& services)
306{
307 verifyIsObject(element);
308 unsigned int propertyCount{0};
309
310 // Optional comments property; value not stored
311 if (element.contains("comments"))
312 {
313 ++propertyCount;
314 }
315
316 // Required type property
317 const json& typeElement = getRequiredProperty(element, "type");
318 std::string type = parseString(typeElement, false, variables);
319 ++propertyCount;
320
321 // Required i2c_interface property
322 const json& i2cInterfaceElement =
323 getRequiredProperty(element, "i2c_interface");
324 auto [bus, address] = parseI2CInterface(i2cInterfaceElement, variables);
325 ++propertyCount;
326
327 // Required power_control_gpio_name property
328 const json& powerControlGPIONameElement =
329 getRequiredProperty(element, "power_control_gpio_name");
330 std::string powerControlGPIOName =
331 parseString(powerControlGPIONameElement, false, variables);
332 ++propertyCount;
333
334 // Required power_good_gpio_name property
335 const json& powerGoodGPIONameElement =
336 getRequiredProperty(element, "power_good_gpio_name");
337 std::string powerGoodGPIOName =
338 parseString(powerGoodGPIONameElement, false, variables);
339 ++propertyCount;
340
341 // Required rails property
342 const json& railsElement = getRequiredProperty(element, "rails");
343 std::vector<std::unique_ptr<Rail>> rails =
344 parseRailArray(railsElement, variables);
345 ++propertyCount;
346
347 // Verify no invalid properties exist
348 verifyPropertyCount(element, propertyCount);
349
350 if (type == UCD90160Device::deviceName)
351 {
352 return std::make_unique<UCD90160Device>(
353 bus, address, powerControlGPIOName, powerGoodGPIOName,
354 std::move(rails), services);
355 }
356 else if (type == UCD90320Device::deviceName)
357 {
358 return std::make_unique<UCD90320Device>(
359 bus, address, powerControlGPIOName, powerGoodGPIOName,
360 std::move(rails), services);
361 }
362 throw std::invalid_argument{"Invalid power sequencer type: " + type};
363}
364
365std::vector<std::unique_ptr<PowerSequencerDevice>> parsePowerSequencerArray(
366 const nlohmann::json& element,
367 const std::map<std::string, std::string>& variables, Services& services)
368{
369 verifyIsArray(element);
370 std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers;
371 for (auto& powerSequencerElement : element)
372 {
373 powerSequencers.emplace_back(
374 parsePowerSequencer(powerSequencerElement, variables, services));
375 }
376 return powerSequencers;
377}
378
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600379std::unique_ptr<Rail> parseRail(
380 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600381{
382 verifyIsObject(element);
383 unsigned int propertyCount{0};
384
385 // Required name property
386 const json& nameElement = getRequiredProperty(element, "name");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600387 std::string name = parseString(nameElement, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600388 ++propertyCount;
389
390 // Optional presence property
391 std::optional<std::string> presence{};
392 auto presenceIt = element.find("presence");
393 if (presenceIt != element.end())
394 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600395 presence = parseString(*presenceIt, false, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600396 ++propertyCount;
397 }
398
399 // Optional page property
400 std::optional<uint8_t> page{};
401 auto pageIt = element.find("page");
402 if (pageIt != element.end())
403 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600404 page = parseUint8(*pageIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600405 ++propertyCount;
406 }
407
Shawn McCarney16e493a2024-01-29 14:20:32 -0600408 // Optional is_power_supply_rail property
409 bool isPowerSupplyRail{false};
410 auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
411 if (isPowerSupplyRailIt != element.end())
412 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600413 isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt, variables);
Shawn McCarney16e493a2024-01-29 14:20:32 -0600414 ++propertyCount;
415 }
416
Shawn McCarney6a957f62024-01-10 16:15:19 -0600417 // Optional check_status_vout property
418 bool checkStatusVout{false};
419 auto checkStatusVoutIt = element.find("check_status_vout");
420 if (checkStatusVoutIt != element.end())
421 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600422 checkStatusVout = parseBoolean(*checkStatusVoutIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600423 ++propertyCount;
424 }
425
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600426 // Optional compare_voltage_to_limit property
427 bool compareVoltageToLimit{false};
428 auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit");
429 if (compareVoltageToLimitIt != element.end())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600430 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600431 compareVoltageToLimit =
432 parseBoolean(*compareVoltageToLimitIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600433 ++propertyCount;
434 }
435
436 // Optional gpio property
437 std::optional<GPIO> gpio{};
438 auto gpioIt = element.find("gpio");
439 if (gpioIt != element.end())
440 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600441 gpio = parseGPIO(*gpioIt, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600442 ++propertyCount;
443 }
444
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600445 // If check_status_vout or compare_voltage_to_limit property is true, the
446 // page property is required; verify page was specified
447 if ((checkStatusVout || compareVoltageToLimit) && !page.has_value())
Shawn McCarney6a957f62024-01-10 16:15:19 -0600448 {
449 throw std::invalid_argument{"Required property missing: page"};
450 }
451
452 // Verify no invalid properties exist
453 verifyPropertyCount(element, propertyCount);
454
Shawn McCarney16e493a2024-01-29 14:20:32 -0600455 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
Shawn McCarney9ec0d432024-02-09 18:26:00 -0600456 checkStatusVout, compareVoltageToLimit, gpio);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600457}
458
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600459std::vector<std::unique_ptr<Rail>> parseRailArray(
460 const json& element, const std::map<std::string, std::string>& variables)
Shawn McCarney6a957f62024-01-10 16:15:19 -0600461{
462 verifyIsArray(element);
463 std::vector<std::unique_ptr<Rail>> rails;
464 for (auto& railElement : element)
465 {
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600466 rails.emplace_back(parseRail(railElement, variables));
Shawn McCarney6a957f62024-01-10 16:15:19 -0600467 }
468 return rails;
469}
470
471std::vector<std::unique_ptr<Rail>> parseRoot(const json& element)
472{
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600473 std::map<std::string, std::string> variables{};
474
Shawn McCarney6a957f62024-01-10 16:15:19 -0600475 verifyIsObject(element);
476 unsigned int propertyCount{0};
477
478 // Required rails property
479 const json& railsElement = getRequiredProperty(element, "rails");
Shawn McCarney038f2ba2025-11-06 13:32:16 -0600480 std::vector<std::unique_ptr<Rail>> rails =
481 parseRailArray(railsElement, variables);
Shawn McCarney6a957f62024-01-10 16:15:19 -0600482 ++propertyCount;
483
484 // Verify no invalid properties exist
485 verifyPropertyCount(element, propertyCount);
486
487 return rails;
488}
489
Shawn McCarneye02aa3c2025-11-10 12:25:54 -0600490std::map<std::string, std::string> parseVariables(const json& element)
491{
492 verifyIsObject(element);
493
494 std::map<std::string, std::string> variables;
495 std::string name, value;
496 for (const auto& [nameElement, valueElement] : element.items())
497 {
498 name = parseString(nameElement);
499 value = parseString(valueElement);
500 variables.emplace(name, value);
501 }
502 return variables;
503}
504
Shawn McCarney6a957f62024-01-10 16:15:19 -0600505} // namespace internal
506
507} // namespace phosphor::power::sequencer::config_file_parser