1 /**
2  * Copyright © 2020 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 #pragma once
17 
18 #include "action.hpp"
19 #include "and_action.hpp"
20 #include "chassis.hpp"
21 #include "compare_presence_action.hpp"
22 #include "compare_vpd_action.hpp"
23 #include "configuration.hpp"
24 #include "device.hpp"
25 #include "i2c_compare_bit_action.hpp"
26 #include "i2c_compare_byte_action.hpp"
27 #include "i2c_compare_bytes_action.hpp"
28 #include "i2c_interface.hpp"
29 #include "i2c_write_bit_action.hpp"
30 #include "i2c_write_byte_action.hpp"
31 #include "i2c_write_bytes_action.hpp"
32 #include "if_action.hpp"
33 #include "not_action.hpp"
34 #include "or_action.hpp"
35 #include "pmbus_write_vout_command_action.hpp"
36 #include "presence_detection.hpp"
37 #include "rail.hpp"
38 #include "rule.hpp"
39 #include "run_rule_action.hpp"
40 #include "sensor_monitoring.hpp"
41 #include "set_device_action.hpp"
42 
43 #include <nlohmann/json.hpp>
44 
45 #include <cstdint>
46 #include <filesystem>
47 #include <memory>
48 #include <stdexcept>
49 #include <string>
50 #include <tuple>
51 #include <vector>
52 
53 namespace phosphor::power::regulators::config_file_parser
54 {
55 
56 /**
57  * Parses the specified JSON configuration file.
58  *
59  * Returns the corresponding C++ Rule and Chassis objects.
60  *
61  * Throws a ConfigFileParserError if an error occurs.
62  *
63  * @param pathName configuration file path name
64  * @return tuple containing vectors of Rule and Chassis objects
65  */
66 std::tuple<std::vector<std::unique_ptr<Rule>>,
67            std::vector<std::unique_ptr<Chassis>>>
68     parse(const std::filesystem::path& pathName);
69 
70 /*
71  * Internal implementation details for parse()
72  */
73 namespace internal
74 {
75 
76 /**
77  * Returns the specified property of the specified JSON element.
78  *
79  * Throws an invalid_argument exception if the property does not exist.
80  *
81  * @param element JSON element
82  * @param property property name
83  */
84 inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element,
85                                                  const std::string& property)
86 {
87     auto it = element.find(property);
88     if (it == element.end())
89     {
90         throw std::invalid_argument{"Required property missing: " + property};
91     }
92     return *it;
93 }
94 
95 /**
96  * Parses a JSON element containing an action.
97  *
98  * Returns the corresponding C++ Action object.
99  *
100  * Throws an exception if parsing fails.
101  *
102  * @param element JSON element
103  * @return Action object
104  */
105 std::unique_ptr<Action> parseAction(const nlohmann::json& element);
106 
107 /**
108  * Parses a JSON element containing an array of actions.
109  *
110  * Returns the corresponding C++ Action objects.
111  *
112  * Throws an exception if parsing fails.
113  *
114  * @param element JSON element
115  * @return vector of Action objects
116  */
117 std::vector<std::unique_ptr<Action>>
118     parseActionArray(const nlohmann::json& element);
119 
120 /**
121  * Parses a JSON element containing an and action.
122  *
123  * Returns the corresponding C++ AndAction object.
124  *
125  * Throws an exception if parsing fails.
126  *
127  * @param element JSON element
128  * @return AndAction object
129  */
130 std::unique_ptr<AndAction> parseAnd(const nlohmann::json& element);
131 
132 /**
133  * Parses a JSON element containing a bit position (from 0-7).
134  *
135  * Returns the corresponding C++ uint8_t value.
136  *
137  * Throws an exception if parsing fails.
138  *
139  * @param element JSON element
140  * @return uint8_t value
141  */
142 inline uint8_t parseBitPosition(const nlohmann::json& element)
143 {
144     // Verify element contains an integer
145     if (!element.is_number_integer())
146     {
147         throw std::invalid_argument{"Element is not an integer"};
148     }
149     int value = element.get<int>();
150     if ((value < 0) || (value > 7))
151     {
152         throw std::invalid_argument{"Element is not a bit position"};
153     }
154     return static_cast<uint8_t>(value);
155 }
156 
157 /**
158  * Parses a JSON element containing a bit value (0 or 1).
159  *
160  * Returns the corresponding C++ uint8_t value.
161  *
162  * Throws an exception if parsing fails.
163  *
164  * @param element JSON element
165  * @return uint8_t value
166  */
167 inline uint8_t parseBitValue(const nlohmann::json& element)
168 {
169     // Verify element contains an integer
170     if (!element.is_number_integer())
171     {
172         throw std::invalid_argument{"Element is not an integer"};
173     }
174     int value = element.get<int>();
175     if ((value < 0) || (value > 1))
176     {
177         throw std::invalid_argument{"Element is not a bit value"};
178     }
179     return static_cast<uint8_t>(value);
180 }
181 
182 /**
183  * Parses a JSON element containing a boolean.
184  *
185  * Returns the corresponding C++ boolean value.
186  *
187  * Throws an exception if parsing fails.
188  *
189  * @param element JSON element
190  * @return boolean value
191  */
192 inline bool parseBoolean(const nlohmann::json& element)
193 {
194     // Verify element contains a boolean
195     if (!element.is_boolean())
196     {
197         throw std::invalid_argument{"Element is not a boolean"};
198     }
199     return element.get<bool>();
200 }
201 
202 /**
203  * Parses a JSON element containing a chassis.
204  *
205  * Returns the corresponding C++ Chassis object.
206  *
207  * Throws an exception if parsing fails.
208  *
209  * @param element JSON element
210  * @return Chassis object
211  */
212 std::unique_ptr<Chassis> parseChassis(const nlohmann::json& element);
213 
214 /**
215  * Parses a JSON element containing an array of chassis.
216  *
217  * Returns the corresponding C++ Chassis objects.
218  *
219  * Throws an exception if parsing fails.
220  *
221  * @param element JSON element
222  * @return vector of Chassis objects
223  */
224 std::vector<std::unique_ptr<Chassis>>
225     parseChassisArray(const nlohmann::json& element);
226 
227 /**
228  * Parses a JSON element containing a compare_presence action.
229  *
230  * Returns the corresponding C++ ComparePresenceAction object.
231  *
232  * Throws an exception if parsing fails.
233  *
234  * @param element JSON element
235  * @return ComparePresenceAction object
236  */
237 std::unique_ptr<ComparePresenceAction>
238     parseComparePresence(const nlohmann::json& element);
239 
240 /**
241  * Parses a JSON element containing a compare_vpd action.
242  *
243  * Returns the corresponding C++ CompareVPDAction object.
244  *
245  * Throws an exception if parsing fails.
246  *
247  * @param element JSON element
248  * @return CompareVPDAction object
249  */
250 std::unique_ptr<CompareVPDAction>
251     parseCompareVPD(const nlohmann::json& element);
252 
253 /**
254  * Parses a JSON element containing a configuration.
255  *
256  * Returns the corresponding C++ Configuration object.
257  *
258  * Throws an exception if parsing fails.
259  *
260  * @param element JSON element
261  * @return Configuration object
262  */
263 std::unique_ptr<Configuration>
264     parseConfiguration(const nlohmann::json& element);
265 
266 /**
267  * Parses a JSON element containing a device.
268  *
269  * Returns the corresponding C++ Device object.
270  *
271  * Throws an exception if parsing fails.
272  *
273  * @param element JSON element
274  * @return Device object
275  */
276 std::unique_ptr<Device> parseDevice(const nlohmann::json& element);
277 
278 /**
279  * Parses a JSON element containing an array of devices.
280  *
281  * Returns the corresponding C++ Device objects.
282  *
283  * Throws an exception if parsing fails.
284  *
285  * @param element JSON element
286  * @return vector of Device objects
287  */
288 std::vector<std::unique_ptr<Device>>
289     parseDeviceArray(const nlohmann::json& element);
290 
291 /**
292  * Parses a JSON element containing a double (floating point number).
293  *
294  * Returns the corresponding C++ double value.
295  *
296  * Throws an exception if parsing fails.
297  *
298  * @param element JSON element
299  * @return double value
300  */
301 inline double parseDouble(const nlohmann::json& element)
302 {
303     // Verify element contains a number (integer or floating point)
304     if (!element.is_number())
305     {
306         throw std::invalid_argument{"Element is not a number"};
307     }
308     return element.get<double>();
309 }
310 
311 /**
312  * Parses a JSON element containing a byte value expressed as a hexadecimal
313  * string.
314  *
315  * The JSON number data type does not support the hexadecimal format.  For this
316  * reason, hexadecimal byte values are stored as strings in the configuration
317  * file.
318  *
319  * Returns the corresponding C++ uint8_t value.
320  *
321  * Throws an exception if parsing fails.
322  *
323  * @param element JSON element
324  * @return uint8_t value
325  */
326 inline uint8_t parseHexByte(const nlohmann::json& element)
327 {
328     if (!element.is_string())
329     {
330         throw std::invalid_argument{"Element is not a string"};
331     }
332     std::string value = element.get<std::string>();
333 
334     bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) &&
335                  (value.size() < 5) &&
336                  (value.find_first_not_of("0123456789abcdefABCDEF", 2) ==
337                   std::string::npos);
338     if (!isHex)
339     {
340         throw std::invalid_argument{"Element is not hexadecimal string"};
341     }
342     return static_cast<uint8_t>(std::stoul(value, 0, 0));
343 }
344 
345 /**
346  * Parses a JSON element containing an array of byte values expressed as a
347  * hexadecimal strings.
348  *
349  * Returns the corresponding C++ uint8_t values.
350  *
351  * Throws an exception if parsing fails.
352  *
353  * @param element JSON element
354  * @return vector of uint8_t
355  */
356 std::vector<uint8_t> parseHexByteArray(const nlohmann::json& element);
357 
358 /**
359  * Parses a JSON element containing an i2c_compare_bit action.
360  *
361  * Returns the corresponding C++ I2CCompareBitAction object.
362  *
363  * Throws an exception if parsing fails.
364  *
365  * @param element JSON element
366  * @return I2CCompareBitAction object
367  */
368 std::unique_ptr<I2CCompareBitAction>
369     parseI2CCompareBit(const nlohmann::json& element);
370 
371 /**
372  * Parses a JSON element containing an i2c_compare_byte action.
373  *
374  * Returns the corresponding C++ I2CCompareByteAction object.
375  *
376  * Throws an exception if parsing fails.
377  *
378  * @param element JSON element
379  * @return I2CCompareByteAction object
380  */
381 std::unique_ptr<I2CCompareByteAction>
382     parseI2CCompareByte(const nlohmann::json& element);
383 
384 /**
385  * Parses a JSON element containing an i2c_compare_bytes action.
386  *
387  * Returns the corresponding C++ I2CCompareBytesAction object.
388  *
389  * Throws an exception if parsing fails.
390  *
391  * @param element JSON element
392  * @return I2CCompareBytesAction object
393  */
394 std::unique_ptr<I2CCompareBytesAction>
395     parseI2CCompareBytes(const nlohmann::json& element);
396 
397 /**
398  * Parses a JSON element containing an i2c_interface.
399  *
400  * Returns the corresponding C++ i2c::I2CInterface object.
401  *
402  * Throws an exception if parsing fails.
403  *
404  * @param element JSON element
405  * @return i2c::I2CInterface object
406  */
407 std::unique_ptr<i2c::I2CInterface>
408     parseI2CInterface(const nlohmann::json& element);
409 
410 /**
411  * Parses a JSON element containing an i2c_write_bit action.
412  *
413  * Returns the corresponding C++ I2CWriteBitAction object.
414  *
415  * Throws an exception if parsing fails.
416  *
417  * @param element JSON element
418  * @return I2CWriteBitAction object
419  */
420 std::unique_ptr<I2CWriteBitAction>
421     parseI2CWriteBit(const nlohmann::json& element);
422 
423 /**
424  * Parses a JSON element containing an i2c_write_byte action.
425  *
426  * Returns the corresponding C++ I2CWriteByteAction object.
427  *
428  * Throws an exception if parsing fails.
429  *
430  * @param element JSON element
431  * @return I2CWriteByteAction object
432  */
433 std::unique_ptr<I2CWriteByteAction>
434     parseI2CWriteByte(const nlohmann::json& element);
435 
436 /**
437  * Parses a JSON element containing an i2c_write_bytes action.
438  *
439  * Returns the corresponding C++ I2CWriteBytesAction object.
440  *
441  * Throws an exception if parsing fails.
442  *
443  * @param element JSON element
444  * @return I2CWriteBytesAction object
445  */
446 std::unique_ptr<I2CWriteBytesAction>
447     parseI2CWriteBytes(const nlohmann::json& element);
448 
449 /**
450  * Parses a JSON element containing an if action.
451  *
452  * Returns the corresponding C++ IfAction object.
453  *
454  * Throws an exception if parsing fails.
455  *
456  * @param element JSON element
457  * @return IfAction object
458  */
459 std::unique_ptr<IfAction> parseIf(const nlohmann::json& element);
460 
461 /**
462  * Parses a JSON element containing an 8-bit signed integer.
463  *
464  * Returns the corresponding C++ int8_t value.
465  *
466  * Throws an exception if parsing fails.
467  *
468  * @param element JSON element
469  * @return int8_t value
470  */
471 inline int8_t parseInt8(const nlohmann::json& element)
472 {
473     // Verify element contains an integer
474     if (!element.is_number_integer())
475     {
476         throw std::invalid_argument{"Element is not an integer"};
477     }
478     int value = element.get<int>();
479     if ((value < INT8_MIN) || (value > INT8_MAX))
480     {
481         throw std::invalid_argument{"Element is not an 8-bit signed integer"};
482     }
483     return static_cast<int8_t>(value);
484 }
485 
486 /**
487  * Parses a JSON element containing a not action.
488  *
489  * Returns the corresponding C++ NotAction object.
490  *
491  * Throws an exception if parsing fails.
492  *
493  * @param element JSON element
494  * @return NotAction object
495  */
496 std::unique_ptr<NotAction> parseNot(const nlohmann::json& element);
497 
498 /**
499  * Parses a JSON element containing an or action.
500  *
501  * Returns the corresponding C++ OrAction object.
502  *
503  * Throws an exception if parsing fails.
504  *
505  * @param element JSON element
506  * @return OrAction object
507  */
508 std::unique_ptr<OrAction> parseOr(const nlohmann::json& element);
509 
510 /**
511  * Parses a JSON element containing a pmbus_write_vout_command action.
512  *
513  * Returns the corresponding C++ PMBusWriteVoutCommandAction object.
514  *
515  * Throws an exception if parsing fails.
516  *
517  * @param element JSON element
518  * @return PMBusWriteVoutCommandAction object
519  */
520 std::unique_ptr<PMBusWriteVoutCommandAction>
521     parsePMBusWriteVoutCommand(const nlohmann::json& element);
522 
523 /**
524  * Parses a JSON element containing a presence detection operation.
525  *
526  * Returns the corresponding C++ PresenceDetection object.
527  *
528  * Throws an exception if parsing fails.
529  *
530  * @param element JSON element
531  * @return PresenceDetection object
532  */
533 std::unique_ptr<PresenceDetection>
534     parsePresenceDetection(const nlohmann::json& element);
535 
536 /**
537  * Parses a JSON element containing a rail.
538  *
539  * Returns the corresponding C++ Rail object.
540  *
541  * Throws an exception if parsing fails.
542  *
543  * @param element JSON element
544  * @return Rail object
545  */
546 std::unique_ptr<Rail> parseRail(const nlohmann::json& element);
547 
548 /**
549  * Parses a JSON element containing an array of rails.
550  *
551  * Returns the corresponding C++ Rail objects.
552  *
553  * Throws an exception if parsing fails.
554  *
555  * @param element JSON element
556  * @return vector of Rail objects
557  */
558 std::vector<std::unique_ptr<Rail>>
559     parseRailArray(const nlohmann::json& element);
560 
561 /**
562  * Parses the JSON root element of the entire configuration file.
563  *
564  * Returns the corresponding C++ Rule and Chassis objects.
565  *
566  * Throws an exception if parsing fails.
567  *
568  * @param element JSON element
569  * @return tuple containing vectors of Rule and Chassis objects
570  */
571 std::tuple<std::vector<std::unique_ptr<Rule>>,
572            std::vector<std::unique_ptr<Chassis>>>
573     parseRoot(const nlohmann::json& element);
574 
575 /**
576  * Parses a JSON element containing a rule.
577  *
578  * Returns the corresponding C++ Rule object.
579  *
580  * Throws an exception if parsing fails.
581  *
582  * @param element JSON element
583  * @return Rule object
584  */
585 std::unique_ptr<Rule> parseRule(const nlohmann::json& element);
586 
587 /**
588  * Parses a JSON element containing an array of rules.
589  *
590  * Returns the corresponding C++ Rule objects.
591  *
592  * Throws an exception if parsing fails.
593  *
594  * @param element JSON element
595  * @return vector of Rule objects
596  */
597 std::vector<std::unique_ptr<Rule>>
598     parseRuleArray(const nlohmann::json& element);
599 
600 /**
601  * Parses the "rule_id" or "actions" property in a JSON element.
602  *
603  * The element must contain one property or the other but not both.
604  *
605  * If the element contains a "rule_id" property, the corresponding C++
606  * RunRuleAction object is returned.
607  *
608  * If the element contains an "actions" property, the corresponding C++ Action
609  * objects are returned.
610  *
611  * Throws an exception if parsing fails.
612  *
613  * @param element JSON element
614  * @return vector of Action objects
615  */
616 std::vector<std::unique_ptr<Action>>
617     parseRuleIDOrActionsProperty(const nlohmann::json& element);
618 
619 /**
620  * Parses a JSON element containing a run_rule action.
621  *
622  * Returns the corresponding C++ RunRuleAction object.
623  *
624  * Throws an exception if parsing fails.
625  *
626  * @param element JSON element
627  * @return RunRuleAction object
628  */
629 std::unique_ptr<RunRuleAction> parseRunRule(const nlohmann::json& element);
630 
631 /**
632  * Parses a JSON element containing a sensor monitoring operation.
633  *
634  * Returns the corresponding C++ SensorMonitoring object.
635  *
636  * Throws an exception if parsing fails.
637  *
638  * @param element JSON element
639  * @return SensorMonitoring object
640  */
641 std::unique_ptr<SensorMonitoring>
642     parseSensorMonitoring(const nlohmann::json& element);
643 
644 /**
645  * Parses a JSON element containing a set_device action.
646  *
647  * Returns the corresponding C++ SetDeviceAction object.
648  *
649  * Throws an exception if parsing fails.
650  *
651  * @param element JSON element
652  * @return SetDeviceAction object
653  */
654 std::unique_ptr<SetDeviceAction> parseSetDevice(const nlohmann::json& element);
655 
656 /**
657  * Parses a JSON element containing a string.
658  *
659  * Returns the corresponding C++ string.
660  *
661  * Throws an exception if parsing fails.
662  *
663  * @param element JSON element
664  * @param isEmptyValid indicates whether an empty string value is valid
665  * @return string value
666  */
667 inline std::string parseString(const nlohmann::json& element,
668                                bool isEmptyValid = false)
669 {
670     if (!element.is_string())
671     {
672         throw std::invalid_argument{"Element is not a string"};
673     }
674     std::string value = element.get<std::string>();
675     if (value.empty() && !isEmptyValid)
676     {
677         throw std::invalid_argument{"Element contains an empty string"};
678     }
679     return value;
680 }
681 
682 /**
683  * Parses a JSON element containing an 8-bit unsigned integer.
684  *
685  * Returns the corresponding C++ uint8_t value.
686  *
687  * Throws an exception if parsing fails.
688  *
689  * @param element JSON element
690  * @return uint8_t value
691  */
692 inline uint8_t parseUint8(const nlohmann::json& element)
693 {
694     // Verify element contains an integer
695     if (!element.is_number_integer())
696     {
697         throw std::invalid_argument{"Element is not an integer"};
698     }
699     int value = element.get<int>();
700     if ((value < 0) || (value > UINT8_MAX))
701     {
702         throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
703     }
704     return static_cast<uint8_t>(value);
705 }
706 
707 /**
708  * Parses a JSON element containing an unsigned integer.
709  *
710  * Returns the corresponding C++ unsigned int value.
711  *
712  * Throws an exception if parsing fails.
713  *
714  * @param element JSON element
715  * @return unsigned int value
716  */
717 inline unsigned int parseUnsignedInteger(const nlohmann::json& element)
718 {
719     // Verify element contains an unsigned integer
720     if (!element.is_number_unsigned())
721     {
722         throw std::invalid_argument{"Element is not an unsigned integer"};
723     }
724     return element.get<unsigned int>();
725 }
726 
727 /**
728  * Verifies that the specified JSON element is a JSON array.
729  *
730  * Throws an invalid_argument exception if the element is not an array.
731  *
732  * @param element JSON element
733  */
734 inline void verifyIsArray(const nlohmann::json& element)
735 {
736     if (!element.is_array())
737     {
738         throw std::invalid_argument{"Element is not an array"};
739     }
740 }
741 
742 /**
743  * Verifies that the specified JSON element is a JSON object.
744  *
745  * Throws an invalid_argument exception if the element is not an object.
746  *
747  * @param element JSON element
748  */
749 inline void verifyIsObject(const nlohmann::json& element)
750 {
751     if (!element.is_object())
752     {
753         throw std::invalid_argument{"Element is not an object"};
754     }
755 }
756 
757 /**
758  * Verifies that the specified JSON element contains the expected number of
759  * properties.
760  *
761  * Throws an invalid_argument exception if the element contains a different
762  * number of properties.  This indicates the element contains an invalid
763  * property.
764  *
765  * @param element JSON element
766  * @param expectedCount expected number of properties in element
767  */
768 inline void verifyPropertyCount(const nlohmann::json& element,
769                                 unsigned int expectedCount)
770 {
771     if (element.size() != expectedCount)
772     {
773         throw std::invalid_argument{"Element contains an invalid property"};
774     }
775 }
776 
777 } // namespace internal
778 
779 } // namespace phosphor::power::regulators::config_file_parser
780