Skip to content

Commit ea6741f

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 ea6741f

25 files changed

+1221
-405
lines changed

contrib/jenkins_tests/gtest.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,20 @@ fi
7272
eval "${sudo_cmd} pkill -9 ${prj_service} 2>/dev/null || true"
7373
eval "${sudo_cmd} ${install_dir}/sbin/${prj_service} --console -v5 &"
7474

75-
# Test with full coverage of the new config
76-
eval "${sudo_cmd} $timeout_exe env XLIO_USE_NEW_CONFIG=1 XLIO_CONFIG_FILE=${WORKSPACE}/tests/gtest/xlio_config_full_coverage.json GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic.xml"
75+
# Test with full coverage of the config-file feature
76+
eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} XLIO_USE_NEW_CONFIG=1 XLIO_CONFIG_FILE=${WORKSPACE}/tests/gtest/xlio_config_full_coverage.json GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic.xml"
7777
rc=$(($rc+$?))
7878

7979
# Exclude EXTRA API tests IPv6
80-
eval "${sudo_cmd} $timeout_exe env GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic-ipv6.xml"
80+
eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio_*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-basic-ipv6.xml"
8181
rc=$(($rc+$?))
8282

8383
# Verify Delegated TCP Timers tests
84-
eval "${sudo_cmd} $timeout_exe env XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate.xml"
84+
eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate.xml"
8585
rc=$(($rc+$?))
8686

8787
# Verify Delegated TCP Timers tests IPv6
88-
eval "${sudo_cmd} $timeout_exe env XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate-ipv6.xml"
88+
eval "${sudo_cmd} $timeout_exe env WORKSPACE=${WORKSPACE} XLIO_RX_POLL_ON_TX_TCP=1 XLIO_TCP_ABORT_ON_CLOSE=1 XLIO_TCP_CTL_THREAD=delegate GTEST_TAP=2 LD_PRELOAD=$gtest_lib $gtest_app $gtest_opt_ipv6 --gtest_filter=-xlio*:-ultra* --gtest_output=xml:${WORKSPACE}/${prefix}/test-delegate-ipv6.xml"
8989
rc=$(($rc+$?))
9090

9191
if [[ -z "${MANUAL_RUN}" ]]; then

src/core/Makefile.am

Lines changed: 2 additions & 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
\
@@ -344,6 +345,7 @@ libxlio_la_SOURCES := \
344345
util/data_updater.h \
345346
\
346347
$(top_builddir)/third_party/legacy_config_parser/config_parser.h \
348+
config_printer.h \
347349
main.h \
348350
xlio.h \
349351
xlio_extra.h \

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::nullopt;
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ 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>
74+
title; /**< Title of the parameter as defined in schema */
7375

7476
// Pre-parsed component configurations
7577
memory_size_extension_config_t memory_cfg; /**< Memory size transformation configuration */
@@ -120,6 +122,7 @@ class schema_analyzer {
120122
std::type_index determine_value_type();
121123
std::experimental::optional<std::experimental::any> determine_default_value(
122124
std::type_index type);
125+
std::experimental::optional<std::string> determine_title();
123126

124127
// Component configuration methods
125128
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: 47 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 +
228+
", not one of: [" + valid_values + "]");
200229
}
201230

202231
std::experimental::any parameter_descriptor::get_value(bool val) const
@@ -226,6 +255,23 @@ 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(
259+
int64_t val, const std::string &default_value) const
260+
{
261+
if (m_string_mapping.empty()) {
262+
return std::to_string(val);
263+
}
264+
265+
for (const auto &mapping : m_string_mapping) {
266+
if ((mapping.second.type() == typeid(int64_t)) &&
267+
(std::experimental::any_cast<int64_t>(mapping.second) == val)) {
268+
return mapping.first;
269+
}
270+
}
271+
272+
return default_value;
273+
}
274+
229275
std::experimental::any parameter_descriptor::get_value(int64_t val) const
230276
{
231277
if (m_type != typeid(int64_t)) {

0 commit comments

Comments
 (0)