Skip to content

Commit e9fb0b7

Browse files
Issue: 4556521 Unified Configuration: missing fields
- Exsposed the config registry for use by other parts of the code - Refactored the display of non-default configurations to scan the config registry Signed-off-by: Oren Shemesh <[email protected]>
1 parent a72ad32 commit e9fb0b7

23 files changed

+1165
-419
lines changed

src/core/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ libxlio_la_SOURCES := \
197197
\
198198
$(top_builddir)/src/core/config/descriptor_providers/xlio_config_schema.h \
199199
\
200+
config_printer.cpp \
200201
libxlio.c \
201202
main.cpp \
202203
\

src/core/config/config_registry.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ void config_registry::initialize_registry(std::queue<std::unique_ptr<loader>> &&
117117
key + "': expected " +
118118
get_user_friendly_type_name(param_desc.type()) + ", got " +
119119
get_user_friendly_type_name(value.type()));
120+
} catch (const std::runtime_error &e) {
121+
throw_xlio_exception("In '" + loader->source() + "': " + e.what());
120122
}
121123
}
122124

src/core/config/config_registry.h

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -69,64 +69,41 @@ class config_registry {
6969
std::vector<std::string> get_sources() const;
7070

7171
/**
72-
* @brief Gets default value for non-integer types
72+
* @brief Gets default value
7373
* @tparam T Value type
7474
* @param key Configuration parameter key
7575
* @return Default value for the parameter
7676
*/
77-
template <typename T>
78-
typename std::enable_if<!is_integer<T>::value, T>::type get_default_value(
79-
const std::string &key) const
80-
{
81-
return get_value_impl<T>(
82-
key, [this](const std::string &k) { return get_default_value_as_any(k); });
83-
}
84-
85-
/**
86-
* @brief Gets default value for integer types with bounds checking
87-
* @tparam T Integer type
88-
* @param key Configuration parameter key
89-
* @return Default value for the parameter
90-
* @note For enums, use int instead since C++14 doesn't support bound checking for enums
91-
*/
92-
template <typename T>
93-
typename std::enable_if<is_integer<T>::value, T>::type get_default_value(
94-
const std::string &key) const
77+
template <typename T> T get_default_value(const std::string &key) const
9578
{
9679
return get_value_impl<T>(
9780
key, [this](const std::string &k) { return get_default_value_as_any(k); });
9881
}
9982

10083
/**
101-
* @brief Gets configured value for non-integer types
84+
* @brief Gets configured value. For integer types, also does bounds checking
10285
* @tparam T Value type
10386
* @param key Configuration parameter key
10487
* @return Current value for the parameter
10588
*/
106-
template <typename T>
107-
typename std::enable_if<!is_integer<T>::value, T>::type get_value(const std::string &key) const
89+
template <typename T> T get_value(const std::string &key) const
10890
{
10991
return get_value_impl<T>(key, [this](const std::string &k) { return get_value_as_any(k); });
11092
}
11193

11294
/**
113-
* @brief Gets configured value for integer types with bounds checking
114-
* @tparam T Integer type
95+
* @brief Gets configured value as any
11596
* @param key Configuration parameter key
116-
* @return Current value for the parameter
117-
* @note For enums, use int instead since C++14 doesn't support bound checking for enums
97+
* @return Current value for the parameter, as any
11898
*/
119-
template <typename T>
120-
typename std::enable_if<is_integer<T>::value, T>::type get_value(const std::string &key) const
121-
{
122-
return get_value_impl<T>(key, [this](const std::string &k) { return get_value_as_any(k); });
123-
}
99+
std::experimental::any get_value_as_any(const std::string &key) const;
100+
101+
const config_descriptor &get_config_descriptor() const { return m_config_descriptor; }
124102

125103
private:
126104
std::map<std::string, std::experimental::any> m_config_data;
127105
config_descriptor m_config_descriptor;
128106
std::vector<std::string> m_sources;
129-
std::experimental::any get_value_as_any(const std::string &key) const;
130107
std::experimental::any get_default_value_as_any(const std::string &key) const;
131108
void initialize_registry(std::queue<std::unique_ptr<loader>> &&value_loaders,
132109
std::unique_ptr<descriptor_provider> descriptor_provider);

src/core/config/descriptor_providers/json_descriptor_provider.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ std::unique_ptr<parameter_descriptor> json_descriptor_provider::create_descripto
152152
// Create parameter descriptor with default value
153153
auto descriptor = std::make_unique<parameter_descriptor>(*analysis.default_value);
154154

155+
descriptor->set_title(analysis.title);
156+
155157
// Apply constraints if present
156158
if (analysis.needs_constraint_validation()) {
157159
apply_constraints(descriptor.get(), analysis.constraint_cfg);

src/core/config/descriptor_providers/schema_analyzer.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <stdexcept>
1212
#include <functional>
1313
#include <algorithm>
14+
#include "vlogger/vlogger.h"
1415

1516
static void for_each_oneof_option(json_object *one_of_field,
1617
std::function<void(json_object *)> func)
@@ -161,6 +162,25 @@ std::experimental::optional<std::experimental::any> schema_analyzer::determine_d
161162
return json_utils::to_any_value(default_field);
162163
}
163164

165+
std::experimental::optional<std::string> schema_analyzer::determine_title()
166+
{
167+
json_object *title_field =
168+
json_utils::try_get_field(m_property_obj, config_strings::schema::JSON_TITLE);
169+
if (!title_field) {
170+
// Enforce title definition only for leafs and arrays. Objects are exempt.
171+
if (determine_value_type() != typeid(json_object *)) {
172+
throw_xlio_exception("Title must be a defined for: " + m_path + " - " +
173+
std::to_string(json_object_get_type(title_field)));
174+
}
175+
return std::experimental::optional<std::string>();
176+
}
177+
if (json_object_get_type(title_field) != json_type_string) {
178+
throw_xlio_exception("Title must be a string for: " + m_path + " - " +
179+
std::to_string(json_object_get_type(title_field)));
180+
}
181+
return std::experimental::optional<std::string>(json_object_get_string(title_field));
182+
}
183+
164184
memory_size_extension_config_t schema_analyzer::analyze_memory_size_extension_config()
165185
{
166186
if (!has_memory_size_flag()) {
@@ -397,6 +417,7 @@ schema_analyzer::analysis_result::analysis_result(schema_analyzer &analyzer)
397417
: json_property_type(analyzer.determine_property_type())
398418
, value_type(analyzer.determine_value_type())
399419
, default_value(analyzer.determine_default_value(value_type))
420+
, title(analyzer.determine_title())
400421
, memory_cfg(analyzer.analyze_memory_size_extension_config())
401422
, constraint_cfg(analyzer.analyze_constraint_config())
402423
, enum_cfg(analyzer.analyze_enum_mapping_config())

src/core/config/descriptor_providers/schema_analyzer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class schema_analyzer {
7070
std::type_index value_type; /**< C++ type for the parameter value */
7171
std::experimental::optional<std::experimental::any>
7272
default_value; /**< Default value ready for use */
73+
std::experimental::optional<std::string> title; /**< Title of the parameter as defined in schema */
7374

7475
// Pre-parsed component configurations
7576
memory_size_extension_config_t memory_cfg; /**< Memory size transformation configuration */
@@ -120,6 +121,7 @@ class schema_analyzer {
120121
std::type_index determine_value_type();
121122
std::experimental::optional<std::experimental::any> determine_default_value(
122123
std::type_index type);
124+
std::experimental::optional<std::string> determine_title();
123125

124126
// Component configuration methods
125127
memory_size_extension_config_t analyze_memory_size_extension_config();

src/core/config/descriptors/config_descriptor.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ std::type_index config_descriptor::get_parent_expected_type(const std::string &k
8080
return typeid(std::map<std::string, std::experimental::any>);
8181
}
8282

83+
const config_descriptor::parameter_map_t &config_descriptor::get_parameter_map() const
84+
{
85+
return parameter_map;
86+
}
87+
8388
/**
8489
* @brief Calculates the Levenshtein distance between two strings.
8590
*

src/core/config/descriptors/config_descriptor.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*/
2121
class config_descriptor {
2222
public:
23+
typedef std::map<std::string, parameter_descriptor> parameter_map_t;
24+
2325
/**
2426
* @brief Default constructor
2527
*/
@@ -55,11 +57,18 @@ class config_descriptor {
5557
*/
5658
std::type_index get_parent_expected_type(const std::string &key) const;
5759

60+
/**
61+
* @brief Gets the parameter map, allowing external users to iterate
62+
* over all parameters
63+
* @return The parameter map
64+
*/
65+
const parameter_map_t &get_parameter_map() const;
66+
5867
private:
5968
/**
6069
* @brief Map from parameter name to its descriptor
6170
*/
62-
std::map<std::string, parameter_descriptor> parameter_map;
71+
parameter_map_t parameter_map;
6372

6473
/**
6574
* @brief Set of all parameter keys for efficient prefix-based lookups

src/core/config/descriptors/parameter_descriptor.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@
1212
#include <climits>
1313
#include <sstream>
1414
#include <cctype>
15+
#include <numeric>
16+
17+
void parameter_descriptor::set_title(const std::experimental::optional<std::string> &title)
18+
{
19+
m_title = title;
20+
}
21+
22+
const std::experimental::optional<std::string> &parameter_descriptor::get_title() const
23+
{
24+
return m_title;
25+
}
1526

1627
/**
1728
* @brief Parse memory size string with suffixes (e.g., "4GB", "512MB", "1024KB", "1024B", "5G")
@@ -151,6 +162,7 @@ parameter_descriptor::parameter_descriptor(const parameter_descriptor &pd)
151162
, m_string_mapping(pd.m_string_mapping)
152163
, m_value_transformer(pd.m_value_transformer)
153164
, m_type(pd.m_type)
165+
, m_title(pd.m_title)
154166
{
155167
}
156168

@@ -164,6 +176,11 @@ void parameter_descriptor::set_string_mappings(const std::map<std::string, int64
164176
}
165177
}
166178

179+
bool parameter_descriptor::has_string_mappings() const
180+
{
181+
return !m_string_mapping.empty();
182+
}
183+
167184
void parameter_descriptor::set_value_transformer(value_transformer_t transformer)
168185
{
169186
m_value_transformer = std::move(transformer);
@@ -196,7 +213,19 @@ std::experimental::any parameter_descriptor::convert_string_to_int64(const std::
196213
return it->second;
197214
}
198215

199-
throw std::experimental::bad_any_cast();
216+
// If no string mappings are defined, throw an exception which has no further information
217+
if (m_string_mapping.empty()) {
218+
throw std::experimental::bad_any_cast();
219+
}
220+
221+
// We have string mappings but value is not one of them - make an effort and create a nice error
222+
// message
223+
std::string valid_values = std::accumulate(
224+
next(m_string_mapping.begin()), m_string_mapping.end(), m_string_mapping.begin()->first,
225+
[](const std::string &a, const auto &b) { return a + "," + b.first; });
226+
227+
throw std::runtime_error("Invalid value for " + m_title.value_or("") + ": " + val + ", not one of: [" +
228+
valid_values + "]");
200229
}
201230

202231
std::experimental::any parameter_descriptor::get_value(bool val) const
@@ -226,6 +255,22 @@ std::experimental::any parameter_descriptor::get_value(const std::string &val) c
226255
throw std::experimental::bad_any_cast();
227256
}
228257

258+
std::string parameter_descriptor::convert_int64_to_mapped_string_or(int64_t val, const std::string &default_value) const
259+
{
260+
if (m_string_mapping.empty()) {
261+
return std::to_string(val);
262+
}
263+
264+
for (const auto &mapping : m_string_mapping) {
265+
if ((mapping.second.type() == typeid(int64_t)) &&
266+
(std::experimental::any_cast<int64_t>(mapping.second) == val)) {
267+
return mapping.first;
268+
}
269+
}
270+
271+
return default_value;
272+
}
273+
229274
std::experimental::any parameter_descriptor::get_value(int64_t val) const
230275
{
231276
if (m_type != typeid(int64_t)) {

src/core/config/descriptors/parameter_descriptor.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <experimental/any>
1010
#include <functional>
1111
#include <map>
12+
#include <experimental/optional>
1213
#include <stdexcept>
1314
#include <string>
1415
#include <typeindex>
@@ -78,12 +79,30 @@ class parameter_descriptor {
7879
*/
7980
void set_string_mappings(const std::map<std::string, int64_t> &mappings);
8081

82+
/**
83+
* @brief returns true if string-to-value mappings were set by set_string_mappings()
84+
* @return true if string-to-value mappings were set by set_string_mappings(), false otherwise
85+
*/
86+
bool has_string_mappings() const;
87+
8188
/**
8289
* @brief Sets a value transformer function
8390
* @param transformer Function to transform input values
8491
*/
8592
void set_value_transformer(value_transformer_t transformer);
8693

94+
/**
95+
* @brief Sets the title of the parameter
96+
* @param title Title of the parameter
97+
*/
98+
99+
void set_title(const std::experimental::optional<std::string> &title);
100+
/**
101+
* @brief Gets the title of the parameter. Note that it may be nullopt if not set
102+
* @return Title of the parameter
103+
*/
104+
const std::experimental::optional<std::string> &get_title() const;
105+
87106
/**
88107
* @brief Validates a value against all constraints
89108
* @param value Value to validate
@@ -138,6 +157,14 @@ class parameter_descriptor {
138157
*/
139158
std::experimental::any get_value(const std::vector<std::experimental::any> &val) const;
140159

160+
/**
161+
* @brief Converts an integer to a string. If string mappings were set by set_string_mappings(), use them.
162+
* @param val Integer value to convert
163+
* @param default_value Default value to return if string mappings were set but the value is not in the mappings
164+
* @return Converted value.
165+
*/
166+
std::string convert_int64_to_mapped_string_or(int64_t val, const std::string &default_value) const;
167+
141168
/**
142169
* @brief Gets the type of the parameter
143170
* @return The type of the parameter
@@ -162,6 +189,7 @@ class parameter_descriptor {
162189
std::map<std::string, std::experimental::any> m_string_mapping; /**< String-to-value mappings */
163190
value_transformer_t m_value_transformer; /**< Value transformation function */
164191
std::type_index m_type;
192+
std::experimental::optional<std::string> m_title; /**< Title of the parameter as defined in schema */
165193

166194
/**
167195
* @brief Parses a memory size string with suffixes (KB, MB, GB)

0 commit comments

Comments
 (0)