Skip to content

Commit 8d82cb0

Browse files
committed
issue: 4207451 Move from env params to config file
MVP: Move from env params to config file. This change introduces a new configuration file that will be used to configure the system. The configuration file is a JSON file that contains all the configuration parameters that were previously passed as environment variables. In the case of a failure to load the configuration file, the system will fall back to the old environment variables method. See HLD and LLD for more information. This patch also adds unit-tests for the new configuration system. Signed-off-by: Tomer Cabouly <[email protected]>
1 parent daa2a8e commit 8d82cb0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4006
-175
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Makefile.in
2121
Makefile
2222
aclocal.m4
2323
autom4te.cache/
24-
config.h
24+
json_descriptor.h
25+
/config.h
2526
config.h.in
2627
config.h.in~
2728
config.log
@@ -77,3 +78,5 @@ docs/man/xlio.7
7778
docs/man/xlio_stats.8
7879
docs/man/xliod.8
7980
tools/daemon/xliod
81+
82+
tests/unit_tests/unit_tests

Makefile.am

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ uninstall-all: uninstall
3838
tests:
3939
$(MAKE)
4040
$(MAKE) -C tests/gtest
41+
$(MAKE) -C tests/unit_tests
4142
$(MAKE) -C tests/latency_test
4243
$(MAKE) -C tests/throughput_test
4344
$(MAKE) -C tests/pps_test

config/m4/json.m4

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#
2+
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3+
# Copyright (c) 2001-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4+
# SPDX-License-Identifier: GPL-2.0-oJSONy or BSD-3-Clause
5+
#
6+
# This software is available to you under a choice of one of two
7+
# licenses. You may choose to be licensed under the terms of the GNU
8+
# General Public License (GPL) Version 2, available from the file
9+
# COPYING in the main directory of this source tree, or the
10+
# BSD license below:
11+
#
12+
# Redistribution and use in source and binary forms, with or
13+
# without modification, are permitted provided that the following
14+
# conditions are met:
15+
#
16+
# - Redistributions of source code must retain the above
17+
# copyright notice, this list of conditions and the following
18+
# disclaimer.
19+
#
20+
# - Redistributions in binary form must reproduce the above
21+
# copyright notice, this list of conditions and the following
22+
# disclaimer in the documentation and/or other materials
23+
# provided with the distribution.
24+
#
25+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29+
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30+
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32+
# SOFTWARE.
33+
#
34+
35+
# json.m4 - Detect json-c package
36+
#
37+
38+
##########################
39+
# Checking json-c library
40+
#
41+
AC_DEFUN([CHECK_JSON_LIB],
42+
[
43+
PKG_CHECK_MODULES([JSON_C], [json-c])
44+
45+
AC_SUBST([LIBJSON_LIBS], "$LIBJSON_C_LIBS")
46+
AC_SUBST([LIBJSON_CFLAGS], "$LIBJSON_C_CFLAGS")
47+
48+
])

configure.ac

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
8484
m4_include([config/m4/func.m4])
8585
m4_include([config/m4/opt.m4])
8686
m4_include([config/m4/verbs.m4])
87+
m4_include([config/m4/json.m4])
8788
m4_include([config/m4/dpcp.m4])
8889
m4_include([config/m4/nl.m4])
8990
m4_include([config/m4/prof.m4])
@@ -312,6 +313,7 @@ AC_CHECK_LIB([rt], [clock_gettime])
312313
AC_CHECK_LIB([pthread], [pthread_create])
313314

314315
CHECK_NL_LIB()
316+
CHECK_JSON_LIB()
315317

316318
dnl===-----------------------------------------------------------------------===
317319
dnl===
@@ -413,6 +415,7 @@ AC_CONFIG_FILES([
413415
src/state_machine/Makefile
414416
tests/Makefile
415417
tests/gtest/Makefile
418+
tests/unit_tests/Makefile
416419
tools/Makefile
417420
tools/daemon/Makefile
418421
docs/man/Makefile

src/core/Makefile.am

+20-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11

22
noinst_LTLIBRARIES = \
33
libconfig_parser.la
4-
libconfig_parser_la_SOURCES =
5-
BUILT_SOURCES =
64

75
AM_LFLAGS = -Plibxlio_yy
86
AM_YFLAGS = -plibxlio_yy -d
@@ -15,10 +13,8 @@ LEX_OUTPUT_ROOT=lex.libxlio_yy
1513
#
1614
#BUILT_SOURCES += config_scanner.c config_parser.h config_parser.c
1715
#libconfig_parser_la_SOURCES += util/config_scanner.l util/config_parser.y
18-
libconfig_parser_la_SOURCES += config_scanner.c config_parser.c
16+
libconfig_parser_la_SOURCES = config_scanner.c config_parser.c
1917

20-
21-
CLEANFILES = $(BUILT_SOURCES)
2218
dist-hook:
2319
cd $(distdir); rm -f $(BUILT_SOURCES)
2420

@@ -56,14 +52,24 @@ libxlio_la_LDFLAGS := $(XLIO_LTO) $(XLIO_PROFILE) -no-undefined \
5652
-version-number @PRJ_LIBRARY_MAJOR@:@PRJ_LIBRARY_MINOR@:@PRJ_LIBRARY_REVISION@
5753

5854
libxlio_la_LIBADD = \
59-
-lrt -ldl -lpthread $(LIBNL_LIBS) $(VERBS_LIBS) $(DPCP_LIBS) $(XLIO_GCOV) \
55+
-lrt -ldl -lpthread $(LIBNL_LIBS) $(VERBS_LIBS) $(DPCP_LIBS) $(XLIO_GCOV) $(JSON_C_LIBS) \
6056
$(top_builddir)/src/utils/libutils.la \
6157
$(top_builddir)/src/vlogger/libvlogger.la \
6258
$(top_builddir)/src/state_machine/libstate_machine.la \
6359
$(top_builddir)/src/stats/libstats.la \
6460
$(top_builddir)/src/core/netlink/libnetlink.la \
6561
$(top_builddir)/src/core/infra/libinfra.la \
6662
libconfig_parser.la
63+
64+
dist_noinst_DATA = config/descriptor_providers/json_descriptor.json
65+
66+
BUILT_SOURCES = config/descriptor_providers/json_descriptor.h
67+
68+
config/descriptor_providers/json_descriptor.h: config/descriptor_providers/json_descriptor.json
69+
xxd -i config/descriptor_providers/json_descriptor.json > $@
70+
71+
CLEANFILES = $(BUILT_SOURCES)
72+
6773

6874
libxlio_la_SOURCES := \
6975
dev/allocator.cpp \
@@ -169,6 +175,14 @@ libxlio_la_SOURCES := \
169175
util/agent.cpp \
170176
util/data_updater.cpp \
171177
\
178+
config/config_manager.cpp \
179+
config/loaders/inline_loader.cpp \
180+
config/loaders/json_loader.cpp \
181+
config/descriptors/parameter_descriptor.cpp \
182+
config/descriptors/config_descriptor.cpp \
183+
config/descriptor_providers/json_descriptor_provider.cpp \
184+
config/utils/json_parsing.cpp \
185+
\
172186
libxlio.c \
173187
main.cpp \
174188
\

src/core/config/config.h

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
#include <experimental/any>
3+
#include <string>
4+
#include <stdexcept>
5+
6+
class config {
7+
public:
8+
virtual ~config() noexcept = default;
9+
template <typename T> T get_value(const std::string &key) const
10+
{
11+
// Defer to a pure virtual function to retrieve the raw value.
12+
std::experimental::any raw_value = get_value_as_any(key);
13+
14+
// Attempt to cast to the requested type T.
15+
try {
16+
return std::experimental::any_cast<T>(raw_value);
17+
} catch (const std::experimental::bad_any_cast &e) {
18+
throw std::runtime_error("config::get_value - Bad any_cast for key: " + key + " - " +
19+
e.what());
20+
}
21+
}
22+
23+
protected:
24+
virtual std::experimental::any get_value_as_any(const std::string &key) const = 0;
25+
};

src/core/config/config_bundle.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#pragma once
2+
3+
#include "descriptors/config_descriptor.h"
4+
5+
struct config_bundle {
6+
std::map<std::string, std::experimental::any> m_config_data;
7+
config_descriptor m_config_descriptor;
8+
};

src/core/config/config_manager.cpp

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#include "config_manager.h"
2+
#include "loaders/inline_loader.h"
3+
#include "loaders/json_loader.h"
4+
#include "descriptor_providers/json_descriptor_provider.h"
5+
#include "core/util/xlio_exception.h"
6+
7+
#include <fstream>
8+
9+
static bool cmdline_contains(const std::string &pattern)
10+
{
11+
std::ifstream cmdline("/proc/self/cmdline", std::ios::binary);
12+
std::string content;
13+
14+
if (cmdline.is_open()) {
15+
std::string arg;
16+
while (std::getline(cmdline, arg, '\0')) {
17+
content += arg + ' ';
18+
}
19+
}
20+
21+
return content.find(pattern) != std::string::npos;
22+
}
23+
24+
config_manager::config_manager()
25+
{
26+
std::queue<std::unique_ptr<loader>> loaders;
27+
28+
const char *inline_env = std::getenv("XLIO_INLINE_CONFIG");
29+
if (inline_env) {
30+
loaders.push(std::make_unique<inline_loader>("XLIO_INLINE_CONFIG"));
31+
}
32+
33+
try {
34+
const char *custom_path = std::getenv("XLIO_CUSTOM_CONFIG_FILE");
35+
if (custom_path) {
36+
loaders.push(std::make_unique<json_loader>(custom_path));
37+
} else {
38+
loaders.push(std::make_unique<json_loader>("/etc/libxlio_config.json"));
39+
}
40+
} catch (const xlio_exception &) {
41+
// If the file is not found, we can continue with the inline config or the deprecated
42+
// method.
43+
}
44+
45+
// legacy method - config manager is not relevant for this case
46+
if (cmdline_contains("XLIO_") && loaders.empty()) {
47+
throw_xlio_exception("Aborting config manager initialization. Legacy method chosen.");
48+
}
49+
50+
initialize_manager(std::move(loaders), std::make_unique<json_descriptor_provider>());
51+
}
52+
53+
void config_manager::initialize_manager(std::queue<std::unique_ptr<loader>> &&value_loaders,
54+
std::unique_ptr<descriptor_provider> descriptor_provider)
55+
{
56+
if (value_loaders.empty() || !descriptor_provider) {
57+
throw_xlio_exception("loader/descriptor_provider cannot be null");
58+
}
59+
60+
// 1) Load raw config data - first in queue means higher priority.
61+
std::map<std::string, std::experimental::any> aggregated_config_data;
62+
while (!value_loaders.empty()) {
63+
auto loaded_data = value_loaders.front()->load_all();
64+
aggregated_config_data.insert(loaded_data.begin(), loaded_data.end());
65+
66+
value_loaders.pop();
67+
}
68+
69+
m_config_bundle.m_config_data = std::move(aggregated_config_data);
70+
71+
// 2) Load or build the descriptor structure
72+
m_config_bundle.m_config_descriptor = descriptor_provider->load_descriptors();
73+
74+
// 3) Validate all settings on construction (fail fast if invalid)
75+
validate_config();
76+
}
77+
78+
config_manager::config_manager(std::queue<std::unique_ptr<loader>> &&value_loaders,
79+
std::unique_ptr<descriptor_provider> descriptor_provider)
80+
{
81+
initialize_manager(std::move(value_loaders), std::move(descriptor_provider));
82+
}
83+
84+
// Validate all config items against their descriptors & constraints.
85+
// A real application might do more elaborate error handling/logging.
86+
void config_manager::validate_config() const
87+
{
88+
// For each key-value in config_data, see if we have descriptor constraints
89+
for (auto const &kv : m_config_bundle.m_config_data) {
90+
const auto &key = kv.first;
91+
const auto &value = kv.second;
92+
93+
// Retrieve the descriptor (could be empty if not found)
94+
auto param_desc = m_config_bundle.m_config_descriptor.get_parameter(key);
95+
// If there's a specific type required, check constraints
96+
if (!param_desc.validate(value)) {
97+
// Throw an exception if validation fails
98+
throw_xlio_exception("Validation failed for key: " + key);
99+
}
100+
}
101+
}
102+
103+
std::experimental::any config_manager::get_value_as_any(const std::string &key) const
104+
{
105+
// Try to find the key in the loaded data
106+
auto it = m_config_bundle.m_config_data.find(key);
107+
if (it == m_config_bundle.m_config_data.end()) {
108+
// Not found in data. Fallback to descriptor's default.
109+
return m_config_bundle.m_config_descriptor.get_parameter(key).default_value();
110+
}
111+
112+
return (it->second);
113+
}

src/core/config/config_manager.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include "config.h"
4+
#include "loaders/loader.h"
5+
#include "config_bundle.h"
6+
#include "descriptor_providers/descriptor_provider.h"
7+
#include <memory>
8+
#include <queue>
9+
10+
class config_manager : public config {
11+
public:
12+
config_manager(std::queue<std::unique_ptr<loader>> &&value_loaders,
13+
std::unique_ptr<descriptor_provider> descriptor_provider);
14+
15+
config_manager();
16+
~config_manager() override = default;
17+
18+
protected:
19+
std::experimental::any get_value_as_any(const std::string &key) const override;
20+
21+
private:
22+
config_bundle m_config_bundle;
23+
24+
void validate_config() const;
25+
void initialize_manager(std::queue<std::unique_ptr<loader>> &&value_loaders,
26+
std::unique_ptr<descriptor_provider> descriptor_provider);
27+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
#include "../descriptors/config_descriptor.h"
3+
4+
class descriptor_provider {
5+
public:
6+
virtual ~descriptor_provider() = default;
7+
8+
// The interface requires a config_descriptor object to be returned.
9+
virtual config_descriptor load_descriptors() = 0;
10+
};

0 commit comments

Comments
 (0)