Add midplane network in systemd generator

Signed-off-by: Ze Gan <ganze718@gmail.com>
This commit is contained in:
Ze Gan 2024-02-01 11:20:06 +08:00
parent 0f4a5b3d87
commit 8b163b1ccf
7 changed files with 653 additions and 60 deletions

View File

@ -3,14 +3,14 @@ CFLAGS += -std=gnu99 -D_GNU_SOURCE
CXX=g++ CXX=g++
CXXFLAGS += -std=c++11 -D_GNU_SOURCE CXXFLAGS += -std=c++11 -D_GNU_SOURCE
LDFLAGS += -lpthread -lboost_filesystem -lboost_system -lgtest LDFLAGS += -lpthread -lboost_filesystem -lboost_system -lgtest -ljson-c
BINARY = systemd-sonic-generator BINARY = systemd-sonic-generator
$(BINARY): systemd-sonic-generator.c $(BINARY): systemd-sonic-generator.c
rm -f ./systemd-sonic-generator rm -f ./systemd-sonic-generator
$(CC) $(CFLAGS) -o $@ $^ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
install: $(BINARY) install: $(BINARY)
mkdir -p $(DESTDIR) mkdir -p $(DESTDIR)

View File

@ -12,6 +12,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <nlohmann/json.hpp>
#include "systemd-sonic-generator.h" #include "systemd-sonic-generator.h"
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@ -19,7 +20,7 @@ namespace fs = boost::filesystem;
namespace SSGTest { namespace SSGTest {
#define IS_MULTI_ASIC(x) ((x) > 1) #define IS_MULTI_ASIC(x) ((x) > 1)
#define IS_SINGLE_ASIC(x) ((x) <= 1) #define IS_SINGLE_ASIC(x) ((x) <= 1)
#define NUM_UNIT_FILES 6 #define NUM_UNIT_FILES 9
/* /*
* This test class uses following directory hierarchy for input and output * This test class uses following directory hierarchy for input and output
@ -41,10 +42,12 @@ namespace SSGTest {
const std::string TEST_ROOT_DIR = "tests/ssg-test/"; const std::string TEST_ROOT_DIR = "tests/ssg-test/";
const std::string TEST_UNIT_FILE_PREFIX = TEST_ROOT_DIR + "systemd/"; const std::string TEST_UNIT_FILE_PREFIX = TEST_ROOT_DIR + "systemd/";
const std::string TEST_ASIC_CONF_FORMAT = TEST_ROOT_DIR + "%s/asic.conf"; const std::string TEST_ASIC_CONF_FORMAT = TEST_ROOT_DIR + "%s/asic.conf";
const std::string TEST_PLATFORM_CONF_FORMAT = TEST_ROOT_DIR + "%s/platform.json";
const std::string TEST_MACHINE_CONF = TEST_ROOT_DIR + "machine.conf"; const std::string TEST_MACHINE_CONF = TEST_ROOT_DIR + "machine.conf";
const std::string TEST_PLATFORM_DIR = TEST_ROOT_DIR + "test_platform/"; const std::string TEST_PLATFORM_DIR = TEST_ROOT_DIR + "test_platform/";
const std::string TEST_ASIC_CONF = TEST_PLATFORM_DIR + "asic.conf"; const std::string TEST_ASIC_CONF = TEST_PLATFORM_DIR + "asic.conf";
const std::string TEST_PLATFORM_CONF = TEST_PLATFORM_DIR + "platform.json";
const std::string TEST_OUTPUT_DIR = TEST_ROOT_DIR + "generator/"; const std::string TEST_OUTPUT_DIR = TEST_ROOT_DIR + "generator/";
@ -61,6 +64,9 @@ const std::vector<std::string> generated_services = {
"test.service", /* A single instance test service "test.service", /* A single instance test service
to test dependency creation */ to test dependency creation */
"test.timer", /* A timer service */ "test.timer", /* A timer service */
"midplane-network.service", /* A midplane network service for smart switch*/
"database.service", /* A database service*/
"database@.service", /* A database service for multi instances */
}; };
static std::mutex g_ssg_test_mutex; static std::mutex g_ssg_test_mutex;
@ -163,6 +169,21 @@ class SsgFunctionTest : public SystemdSonicGeneratorFixture {
private: private:
}; };
struct SsgMainConfig {
int num_asics;
bool is_smart_switch_npu;
bool is_smart_switch_dpu;
int num_dpus;
SsgMainConfig() {
num_asics = 0;
is_smart_switch_npu = false;
is_smart_switch_dpu = false;
num_dpus = 0;
}
};
/* /*
* class SsgMainTest * class SsgMainTest
* Implements functions to test ssg_main routine. * Implements functions to test ssg_main routine.
@ -228,10 +249,10 @@ class SsgMainTest : public SsgFunctionTest {
void validate_output_unit_files(std::vector<std::string> strs, void validate_output_unit_files(std::vector<std::string> strs,
std::string target, std::string target,
bool expected_result, bool expected_result,
int num_asics) { int num_instances) {
for (std::string str : strs) { for (std::string str : strs) {
bool finished = false; bool finished = false;
for (int i = 0 ; i < num_asics && !finished; ++i) { for (int i = 0 ; i < num_instances && !finished; ++i) {
auto str_t = str; auto str_t = str;
if (is_multi_instance(str)) { if (is_multi_instance(str)) {
/* insert instance id in string */ /* insert instance id in string */
@ -250,7 +271,7 @@ class SsgMainTest : public SsgFunctionTest {
/* /*
* This function validates the generated dependencies in a Unit File. * This function validates the generated dependencies in a Unit File.
*/ */
void validate_depedency_in_unit_file(int num_asics) { void validate_depedency_in_unit_file(const SsgMainConfig &cfg) {
std::string test_service = "test.service"; std::string test_service = "test.service";
/* Validate Unit file dependency creation for multi instance /* Validate Unit file dependency creation for multi instance
@ -258,53 +279,80 @@ class SsgMainTest : public SsgFunctionTest {
* system but not present for single asic system. * system but not present for single asic system.
*/ */
validate_output_dependency_list(multi_asic_dependency_list, validate_output_dependency_list(multi_asic_dependency_list,
test_service, IS_MULTI_ASIC(num_asics), num_asics); test_service, IS_MULTI_ASIC(cfg.num_asics), cfg.num_asics);
/* This section handles a tricky scenario.
* When the number of DPUs (Data Processing Units) is greater than 0,
* the dependency list will be split. Otherwise, it remains in one line.
* Despite the split, the final result remains equivalent.
*/
if (cfg.num_dpus > 0) {
/* Validate Unit file dependency creation for single instance
* services. These entries should not be present for multi asic
* system but present for single asic system.
*/
validate_output_dependency_list(single_asic_dependency_list_split,
test_service, IS_SINGLE_ASIC(cfg.num_asics), cfg.num_asics);
} else {
/* Validate Unit file dependency creation for single instance /* Validate Unit file dependency creation for single instance
* services. These entries should not be present for multi asic * services. These entries should not be present for multi asic
* system but present for single asic system. * system but present for single asic system.
*/ */
validate_output_dependency_list(single_asic_dependency_list, validate_output_dependency_list(single_asic_dependency_list,
test_service, IS_SINGLE_ASIC(num_asics), num_asics); test_service, IS_SINGLE_ASIC(cfg.num_asics), cfg.num_asics);
}
/* Validate Unit file dependency creation for single instance /* Validate Unit file dependency creation for single instance
* common services. These entries should not be present for multi * common services. These entries should not be present for multi
* and single asic system. * and single asic system.
*/ */
validate_output_dependency_list(common_dependency_list, validate_output_dependency_list(common_dependency_list,
test_service, true, num_asics); test_service, true, cfg.num_asics);
/* Validate Unit file dependency creation for smart switch
* services. These entries should not be present for npu
* and dpu of smart switch.
*/
validate_output_dependency_list(smart_switch_dependency_list,
"database.service", cfg.is_smart_switch_dpu || cfg.is_smart_switch_npu, cfg.num_asics);
validate_output_dependency_list(smart_switch_dependency_list,
"database@.service", cfg.is_smart_switch_npu, cfg.num_asics);
} }
/* /*
* This function validates the list of generated Service Unit Files. * This function validates the list of generated Service Unit Files.
*/ */
void validate_service_file_generated_list(int num_asics) { void validate_service_file_generated_list(const SsgMainConfig &cfg) {
std::string test_target = "multi-user.target.wants"; std::string test_target = "multi-user.target.wants";
validate_output_unit_files(multi_asic_service_list, validate_output_unit_files(multi_asic_service_list,
test_target, IS_MULTI_ASIC(num_asics), num_asics); test_target, IS_MULTI_ASIC(cfg.num_asics), cfg.num_asics);
validate_output_unit_files(single_asic_service_list, validate_output_unit_files(single_asic_service_list,
test_target, IS_SINGLE_ASIC(num_asics), num_asics); test_target, IS_SINGLE_ASIC(cfg.num_asics), cfg.num_asics);
validate_output_unit_files(common_service_list, validate_output_unit_files(common_service_list,
test_target, true, num_asics); test_target, true, cfg.num_asics);
validate_output_unit_files(smart_switch_service_list,
test_target, cfg.is_smart_switch_dpu || cfg.is_smart_switch_npu, cfg.num_dpus);
} }
/* ssg_main test routine. /* ssg_main test routine.
* input: num_asics number of asics * input: num_asics number of asics
*/ */
void ssg_main_test(int num_asics) { void ssg_main_test(const SsgMainConfig &cfg) {
FILE* fp; FILE* fp;
std::vector<char*> argv_; std::vector<char*> argv_;
std::vector<std::string> arguments = { std::vector<std::string> arguments = {
"ssg_main", "ssg_main",
TEST_OUTPUT_DIR.c_str() TEST_OUTPUT_DIR.c_str()
}; };
std::string num_asic_str = "NUM_ASIC=" + std::to_string(num_asics); std::string num_asic_str = "NUM_ASIC=" + std::to_string(cfg.num_asics);
std::string unit_file_path = fs::current_path().string() + "/" +TEST_UNIT_FILE_PREFIX; std::string unit_file_path = fs::current_path().string() + "/" +TEST_UNIT_FILE_PREFIX;
g_unit_file_prefix = unit_file_path.c_str(); g_unit_file_prefix = unit_file_path.c_str();
g_config_file = TEST_CONFIG_FILE.c_str(); g_config_file = TEST_CONFIG_FILE.c_str();
g_machine_config_file = TEST_MACHINE_CONF.c_str(); g_machine_config_file = TEST_MACHINE_CONF.c_str();
g_asic_conf_format = TEST_ASIC_CONF_FORMAT.c_str(); g_asic_conf_format = TEST_ASIC_CONF_FORMAT.c_str();
g_platform_file_format = TEST_PLATFORM_CONF_FORMAT.c_str();
/* Set NUM_ASIC value in asic.conf */ /* Set NUM_ASIC value in asic.conf */
fp = fopen(TEST_ASIC_CONF.c_str(), "w"); fp = fopen(TEST_ASIC_CONF.c_str(), "w");
@ -312,6 +360,29 @@ class SsgMainTest : public SsgFunctionTest {
fputs(num_asic_str.c_str(), fp); fputs(num_asic_str.c_str(), fp);
fclose(fp); fclose(fp);
/* Set platform file for smart switch */
if (cfg.is_smart_switch_dpu || cfg.is_smart_switch_npu) {
nlohmann::json platform_config;
if (cfg.is_smart_switch_dpu) {
ASSERT_EQ(cfg.num_dpus, 0);
ASSERT_EQ(cfg.is_smart_switch_npu, false);
platform_config["DPU"] = nlohmann::json::object();
}
else if (cfg.is_smart_switch_npu) {
ASSERT_EQ(cfg.is_smart_switch_dpu, false);
nlohmann::json::array_t dpus;
for (int i = 0; i < cfg.num_dpus; i++) {
dpus.push_back(nlohmann::json::object());
}
platform_config["DPUS"] = dpus;
}
fp = fopen(TEST_PLATFORM_CONF.c_str(), "w");
ASSERT_NE(fp, nullptr);
fputs(platform_config.dump().c_str(), fp);
fclose(fp);
}
/* Create argv list for ssg_main. */ /* Create argv list for ssg_main. */
for (const auto& arg : arguments) { for (const auto& arg : arguments) {
argv_.push_back((char*)arg.data()); argv_.push_back((char*)arg.data());
@ -322,10 +393,10 @@ class SsgMainTest : public SsgFunctionTest {
EXPECT_EQ(ssg_main(argv_.size(), argv_.data()), 0); EXPECT_EQ(ssg_main(argv_.size(), argv_.data()), 0);
/* Validate systemd service template creation. */ /* Validate systemd service template creation. */
validate_service_file_generated_list(num_asics); validate_service_file_generated_list(cfg);
/* Validate Test Unit file for dependency creation. */ /* Validate Test Unit file for dependency creation. */
validate_depedency_in_unit_file(num_asics); validate_depedency_in_unit_file(cfg);
} }
/* Save global variables before running tests */ /* Save global variables before running tests */
@ -343,9 +414,12 @@ class SsgMainTest : public SsgFunctionTest {
static const std::vector<std::string> single_asic_service_list; static const std::vector<std::string> single_asic_service_list;
static const std::vector<std::string> multi_asic_service_list; static const std::vector<std::string> multi_asic_service_list;
static const std::vector<std::string> common_service_list; static const std::vector<std::string> common_service_list;
static const std::vector<std::string> smart_switch_service_list;
static const std::vector<std::string> single_asic_dependency_list; static const std::vector<std::string> single_asic_dependency_list;
static const std::vector<std::string> single_asic_dependency_list_split;
static const std::vector<std::string> multi_asic_dependency_list; static const std::vector<std::string> multi_asic_dependency_list;
static const std::vector<std::string> common_dependency_list; static const std::vector<std::string> common_dependency_list;
static const std::vector<std::string> smart_switch_dependency_list;
}; };
/* /*
@ -368,6 +442,7 @@ const std::vector<std::string>
SsgMainTest::multi_asic_service_list = { SsgMainTest::multi_asic_service_list = {
"multi_inst_a@%1%.service", "multi_inst_a@%1%.service",
"multi_inst_b@%1%.service", "multi_inst_b@%1%.service",
"database@%1%.service",
}; };
/* Common Systemd service Unit file list for single and multi asic system. */ /* Common Systemd service Unit file list for single and multi asic system. */
@ -376,7 +451,14 @@ SsgMainTest::common_service_list = {
"multi_inst_a.service", "multi_inst_a.service",
"single_inst.service", "single_inst.service",
"test.service", "test.service",
"database.service",
};
/* Common Systemd service Unit file list for single and multi asic system. */
const std::vector<std::string>
SsgMainTest::smart_switch_service_list = {
"database@dpu%1%.service",
"midplane-network.service",
}; };
/* /*
@ -398,6 +480,13 @@ SsgMainTest::single_asic_dependency_list = {
"After=multi_inst_a.service multi_inst_b.service", "After=multi_inst_a.service multi_inst_b.service",
}; };
/* Systemd service Unit file dependency entries for Single asic system. */
const std::vector<std::string>
SsgMainTest::single_asic_dependency_list_split = {
"After=multi_inst_a.service",
"After=multi_inst_b.service",
};
/* Systemd service Unit file dependency entries for multi asic system. */ /* Systemd service Unit file dependency entries for multi asic system. */
const std::vector<std::string> const std::vector<std::string>
SsgMainTest::multi_asic_dependency_list = { SsgMainTest::multi_asic_dependency_list = {
@ -413,6 +502,12 @@ SsgMainTest::common_dependency_list = {
"Before=single_inst.service", "Before=single_inst.service",
}; };
const std::vector<std::string>
SsgMainTest::smart_switch_dependency_list = {
"Requires=midplane-network.service",
"After=midplane-network.service",
};
/* Test get functions for global vasr*/ /* Test get functions for global vasr*/
TEST_F(SystemdSonicGeneratorFixture, get_global_vars) { TEST_F(SystemdSonicGeneratorFixture, get_global_vars) {
EXPECT_EQ(g_unit_file_prefix, nullptr); EXPECT_EQ(g_unit_file_prefix, nullptr);
@ -460,7 +555,7 @@ TEST_F(SsgFunctionTest, insert_instance_number) {
char input[] = "test@.service"; char input[] = "test@.service";
for (int i = 0; i <= 100; ++i) { for (int i = 0; i <= 100; ++i) {
std::string out = "test@" + std::to_string(i) + ".service"; std::string out = "test@" + std::to_string(i) + ".service";
char* ret = insert_instance_number(input, i); char* ret = insert_instance_number(input, i, "");
ASSERT_NE(ret, nullptr); ASSERT_NE(ret, nullptr);
EXPECT_STREQ(ret, out.c_str()); EXPECT_STREQ(ret, out.c_str());
} }
@ -496,12 +591,22 @@ TEST_F(SsgFunctionTest, get_num_of_asic) {
TEST_F(SsgFunctionTest, get_unit_files) { TEST_F(SsgFunctionTest, get_unit_files) {
g_unit_file_prefix = TEST_UNIT_FILE_PREFIX.c_str(); g_unit_file_prefix = TEST_UNIT_FILE_PREFIX.c_str();
g_config_file = TEST_CONFIG_FILE.c_str(); g_config_file = TEST_CONFIG_FILE.c_str();
char* unit_files[NUM_UNIT_FILES]; char* unit_files[NUM_UNIT_FILES] = { NULL };
int num_unit_files = get_unit_files(unit_files); int num_unit_files = get_unit_files(unit_files);
EXPECT_EQ(num_unit_files, NUM_UNIT_FILES); // Exclude the midplane-network.service which is only used for smart switch
for (std::string service : generated_services) { auto non_smart_switch_generated_services = generated_services;
non_smart_switch_generated_services.erase(
std::remove(non_smart_switch_generated_services.begin(),
non_smart_switch_generated_services.end(),
"midplane-network.service"),
non_smart_switch_generated_services.end());
EXPECT_EQ(num_unit_files, non_smart_switch_generated_services.size());
for (std::string service : non_smart_switch_generated_services) {
bool found = false; bool found = false;
for (auto& unit_file : unit_files) { for (auto& unit_file : unit_files) {
if (unit_file == NULL) {
continue;
}
if(unit_file == service) { if(unit_file == service) {
found = true; found = true;
break; break;
@ -530,18 +635,49 @@ TEST_F(SsgMainTest, ssg_main_argv) {
/* TEST ssg_main() single asic */ /* TEST ssg_main() single asic */
TEST_F(SsgMainTest, ssg_main_single_npu) { TEST_F(SsgMainTest, ssg_main_single_npu) {
ssg_main_test(1); SsgMainConfig cfg;
cfg.num_asics = 1;
ssg_main_test(cfg);
} }
/* TEST ssg_main() multi(10) asic */ /* TEST ssg_main() multi(10) asic */
TEST_F(SsgMainTest, ssg_main_10_npu) { TEST_F(SsgMainTest, ssg_main_10_npu) {
ssg_main_test(10); SsgMainConfig cfg;
cfg.num_asics = 10;
ssg_main_test(cfg);
} }
/* TEST ssg_main() multi(40) asic */ /* TEST ssg_main() multi(40) asic */
TEST_F(SsgMainTest, ssg_main_40_npu) { TEST_F(SsgMainTest, ssg_main_40_npu) {
ssg_main_test(40); SsgMainConfig cfg;
cfg.num_asics = 40;
ssg_main_test(cfg);
} }
TEST_F(SsgMainTest, ssg_main_smart_switch_npu) {
SsgMainConfig cfg;
cfg.num_asics = 1;
cfg.is_smart_switch_npu = true;
cfg.num_dpus = 8;
ssg_main_test(cfg);
}
TEST_F(SsgMainTest, ssg_main_smart_switch_dpu) {
SsgMainConfig cfg;
cfg.num_asics = 1;
cfg.is_smart_switch_dpu = true;
ssg_main_test(cfg);
}
TEST_F(SsgMainTest, ssg_main_smart_switch_double_execution) {
SsgMainConfig cfg;
cfg.num_asics = 1;
cfg.is_smart_switch_npu = true;
cfg.num_dpus = 8;
ssg_main_test(cfg);
ssg_main_test(cfg);
}
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {

View File

@ -8,16 +8,22 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <json-c/json.h>
#define MAX_NUM_TARGETS 48 #define MAX_NUM_TARGETS 48
#define MAX_NUM_INSTALL_LINES 48 #define MAX_NUM_INSTALL_LINES 48
#define MAX_NUM_UNITS 128 #define MAX_NUM_UNITS 128
#define MAX_BUF_SIZE 512 #define MAX_BUF_SIZE 512
#define MAX_PLATFORM_NAME_LEN 64
const char* UNIT_FILE_PREFIX = "/usr/lib/systemd/system/"; const char* UNIT_FILE_PREFIX = "/usr/lib/systemd/system/";
const char* CONFIG_FILE = "/etc/sonic/generated_services.conf"; const char* CONFIG_FILE = "/etc/sonic/generated_services.conf";
const char* MACHINE_CONF_FILE = "/host/machine.conf"; const char* MACHINE_CONF_FILE = "/host/machine.conf";
const char* ASIC_CONF_FORMAT = "/usr/share/sonic/device/%s/asic.conf"; const char* ASIC_CONF_FORMAT = "/usr/share/sonic/device/%s/asic.conf";
const char* PLATFORM_FILE_FORMAT = "/usr/share/sonic/device/%s/platform.json";
const char* DPU_PREFIX = "dpu";
const char* g_unit_file_prefix = NULL; const char* g_unit_file_prefix = NULL;
const char* get_unit_file_prefix() { const char* get_unit_file_prefix() {
@ -39,9 +45,64 @@ const char* get_asic_conf_format() {
return (g_asic_conf_format) ? g_asic_conf_format : ASIC_CONF_FORMAT; return (g_asic_conf_format) ? g_asic_conf_format : ASIC_CONF_FORMAT;
} }
const char* g_platform_file_format = NULL;
const char* get_platform_file_format() {
return (g_platform_file_format) ? g_platform_file_format : PLATFORM_FILE_FORMAT;
}
static int num_asics; static int num_asics;
static char** multi_instance_services; static char** multi_instance_services;
static int num_multi_inst; static int num_multi_inst;
static bool smart_switch_npu;
static bool smart_switch_dpu;
static bool smart_switch;
static size_t num_dpus;
static char* platform = NULL;
static struct json_object *platform_info = NULL;
#ifdef _SSG_UNITTEST
/**
* @brief Cleans up the cache by resetting cache pointers.
*/
void clean_up_cache() {
platform = NULL;
platform_info = NULL;
}
#endif
/**
* Sets the value of a pointer to an invalid memory address.
*
* @param pointer A pointer to a pointer variable.
*/
void set_invalid_pointer(void **pointer) {
*pointer = (void *)-1;
}
/**
* @brief Checks if a pointer is valid.
*
* This function checks if a pointer is valid by verifying that it is not NULL and not equal to (void *)-1.
*
* @param pointer The pointer to be checked.
* @return true if the pointer is valid, false otherwise.
*/
bool is_valid_pointer(void *pointer) {
return pointer != NULL && pointer != (void *)-1;
}
/**
* Checks if a pointer is initialized.
*
* @param pointer The pointer to check.
* @return true if the pointer is not NULL, false otherwise.
*/
bool is_initialized_pointer(void *pointer) {
return pointer != NULL;
}
void strip_trailing_newline(char* str) { void strip_trailing_newline(char* str) {
/*** /***
@ -128,6 +189,45 @@ static bool is_multi_instance_service(char *service_name){
} }
/**
* Checks if a service is a multi-instance service for DPU.
*
* @param service_name The name of the service to check.
* @return true if the service is a multi-instance service for DPU, false otherwise.
*/
static bool is_multi_instance_service_for_dpu(const char *service_name) {
if (!smart_switch_npu) {
return false;
}
const static char* multi_instance_services_for_dpu[] = {"database"};
char *saveptr;
char *tmp_service_name = strdup(service_name);
for (size_t i = 0; i < sizeof(multi_instance_services_for_dpu) /
sizeof(multi_instance_services_for_dpu[0]);
i++) {
char* saveptr;
char* token = strtok_r(tmp_service_name, "@", &saveptr);
if (token) {
if (strstr(token, ".service") != NULL) {
/* If we are here, service_name did not have '@' delimiter but
* contains '.service' */
token = strtok_r(tmp_service_name, ".", &saveptr);
}
}
if (strcmp(tmp_service_name, multi_instance_services_for_dpu[i]) == 0) {
free(tmp_service_name);
return true;
}
}
free(tmp_service_name);
return false;
}
static int get_install_targets_from_line(char* target_string, char* install_type, char* targets[], int existing_targets) { static int get_install_targets_from_line(char* target_string, char* install_type, char* targets[], int existing_targets) {
/*** /***
Helper fuction for get_install_targets Helper fuction for get_install_targets
@ -228,12 +328,18 @@ static void replace_multi_inst_dep(char *src) {
service_name = strdup(word); service_name = strdup(word);
service_name = strtok_r(service_name, ".", &save_ptr2); service_name = strtok_r(service_name, ".", &save_ptr2);
type = strtok_r(NULL, "\n", &save_ptr2); type = strtok_r(NULL, "\n", &save_ptr2);
if (is_multi_instance_service(word)) { if (num_asics > 1 && is_multi_instance_service(word)) {
for(i = 0; i < num_asics; i++) { for(i = 0; i < num_asics; i++) {
snprintf(buf, MAX_BUF_SIZE, "%s=%s@%d.%s\n", snprintf(buf, MAX_BUF_SIZE, "%s=%s@%d.%s\n",
token, service_name, i, type); token, service_name, i, type);
fputs(buf,fp_tmp); fputs(buf,fp_tmp);
} }
} else if (smart_switch_npu && is_multi_instance_service_for_dpu(word)) {
for(i = 0; i < num_dpus; i++) {
snprintf(buf, MAX_BUF_SIZE, "%s=%s@%s%d.%s\n",
token, service_name, DPU_PREFIX, i, type);
fputs(buf,fp_tmp);
}
} else { } else {
snprintf(buf, MAX_BUF_SIZE,"%s=%s.%s\n",token, service_name, type); snprintf(buf, MAX_BUF_SIZE,"%s=%s.%s\n",token, service_name, type);
fputs(buf, fp_tmp); fputs(buf, fp_tmp);
@ -280,7 +386,8 @@ int get_install_targets(char* unit_file, char* targets[]) {
dot_ptr = strchr(instance_name, '.'); dot_ptr = strchr(instance_name, '.');
*dot_ptr = '\0'; *dot_ptr = '\0';
if((num_asics > 1) && (!is_multi_instance_service(instance_name))) { if(((num_asics > 1) && (!is_multi_instance_service(instance_name)))
|| ((num_dpus > 0) && (!is_multi_instance_service_for_dpu(instance_name)))) {
replace_multi_inst_dep(file_path); replace_multi_inst_dep(file_path);
} }
free(instance_name); free(instance_name);
@ -362,6 +469,13 @@ int get_unit_files(char* unit_files[]) {
(num_asics == 1)) { (num_asics == 1)) {
continue; continue;
} }
/* midplane-network service to be started only for smart switch platform */
if ((strcmp(line, "midplane-network.service") == 0) &&
!smart_switch) {
continue;
}
unit_files[num_unit_files] = strdup(line); unit_files[num_unit_files] = strdup(line);
num_unit_files++; num_unit_files++;
} }
@ -374,7 +488,7 @@ int get_unit_files(char* unit_files[]) {
} }
char* insert_instance_number(char* unit_file, int instance) { char* insert_instance_number(char* unit_file, int instance, const char *instance_prefix) {
/*** /***
Adds an instance number to a systemd template name Adds an instance number to a systemd template name
@ -390,12 +504,16 @@ char* insert_instance_number(char* unit_file, int instance) {
return NULL; return NULL;
} }
if (instance_prefix == NULL) {
instance_prefix = "";
}
/*** /***
suffix is "@.service", set suffix=".service" suffix is "@.service", set suffix=".service"
prefix_len is length of "example@" prefix_len is length of "example@"
***/ ***/
prefix_len = ++suffix - unit_file; prefix_len = ++suffix - unit_file;
ret = asprintf(&instance_name, "%.*s%d%s", prefix_len, unit_file, instance, suffix); ret = asprintf(&instance_name, "%.*s%s%d%s", prefix_len, unit_file, instance_prefix, instance, suffix);
if (ret == -1) { if (ret == -1) {
fprintf(stderr, "Error creating instance %d of %s\n", instance, unit_file); fprintf(stderr, "Error creating instance %d of %s\n", instance, unit_file);
return NULL; return NULL;
@ -405,7 +523,7 @@ char* insert_instance_number(char* unit_file, int instance) {
} }
static int create_symlink(char* unit, char* target, char* install_dir, int instance) { static int create_symlink(char* unit, char* target, char* install_dir, int instance, const char *instance_prefix) {
struct stat st; struct stat st;
char src_path[PATH_MAX]; char src_path[PATH_MAX];
char dest_path[PATH_MAX]; char dest_path[PATH_MAX];
@ -420,7 +538,7 @@ static int create_symlink(char* unit, char* target, char* install_dir, int insta
unit_instance = strdup(unit); unit_instance = strdup(unit);
} }
else { else {
unit_instance = insert_instance_number(unit, instance); unit_instance = insert_instance_number(unit, instance, instance_prefix);
} }
strcpy(final_install_dir, install_dir); strcpy(final_install_dir, install_dir);
@ -501,22 +619,31 @@ static int install_unit_file(char* unit_file, char* target, char* install_dir) {
for (int i = 0; i < num_asics; i++) { for (int i = 0; i < num_asics; i++) {
if (strstr(target, "@") != NULL) { if (strstr(target, "@") != NULL) {
target_instance = insert_instance_number(target, i); target_instance = insert_instance_number(target, i, "");
} }
else { else {
target_instance = strdup(target); target_instance = strdup(target);
} }
r = create_symlink(unit_file, target_instance, install_dir, i); r = create_symlink(unit_file, target_instance, install_dir, i, "");
if (r < 0) if (r < 0)
fprintf(stderr, "Error installing %s for target %s\n", unit_file, target_instance); fprintf(stderr, "Error installing %s for target %s\n", unit_file, target_instance);
free(target_instance); free(target_instance);
} }
} else if (num_dpus > 0 && strstr(unit_file, "@") != NULL) {
// If multi-instance service for DPU
// Install each DPU units to the host main instance only,
// E.g. install database@dpu0.service, database@dpu1.service to multi-user.target.wants
// We don't have case like to install xxx@dpu0.service to swss@dpu0.service.wants
for (int i = 0; i < num_dpus; i++) {
r = create_symlink(unit_file, target, install_dir, i, DPU_PREFIX);
if (r < 0)
fprintf(stderr, "Error installing %s for target %s\n", unit_file, target);
} }
else { } else {
r = create_symlink(unit_file, target, install_dir, -1); r = create_symlink(unit_file, target, install_dir, -1, "");
if (r < 0) if (r < 0)
fprintf(stderr, "Error installing %s for target %s\n", unit_file, target); fprintf(stderr, "Error installing %s for target %s\n", unit_file, target);
} }
@ -525,6 +652,59 @@ static int install_unit_file(char* unit_file, char* target, char* install_dir) {
} }
/**
* Retrieves the platform name from the machine configuration file.
* If the platform name is already cached, it returns the cached value.
* If the platform name is not found in the configuration file, it sets the platform pointer to NULL.
*
* @return The platform name if found, otherwise NULL.
*/
const char* get_platform() {
if (is_initialized_pointer(platform)) {
if (is_valid_pointer(platform)) {
return platform;
} else {
return NULL;
}
}
FILE* fp;
char* line = NULL;
char* token;
char* saveptr;
char *tmp_platform = NULL;
static char platform_buffer[MAX_PLATFORM_NAME_LEN + 1];
size_t len = 0;
ssize_t nread;
const char* machine_config_file = get_machine_config_file();
fp = fopen(machine_config_file, "r");
if (fp == NULL) {
fprintf(stderr, "Failed to open %s\n", machine_config_file);
exit(EXIT_FAILURE);
}
while ((nread = getline(&line, &len, fp)) != -1) {
if ((strstr(line, "onie_platform") != NULL) ||
(strstr(line, "aboot_platform") != NULL)) {
token = strtok_r(line, "=", &saveptr);
tmp_platform = strtok_r(NULL, "=", &saveptr);
strip_trailing_newline(tmp_platform);
break;
}
}
if (tmp_platform == NULL) {
set_invalid_pointer((void **)&platform);
return NULL;
}
strncpy(platform_buffer, tmp_platform, sizeof(platform_buffer) - 1);
fclose(fp);
free(line);
platform = platform_buffer;
return platform;
}
int get_num_of_asic() { int get_num_of_asic() {
/*** /***
Determines if the current platform is single or multi-ASIC Determines if the current platform is single or multi-ASIC
@ -532,33 +712,16 @@ int get_num_of_asic() {
FILE *fp; FILE *fp;
char *line = NULL; char *line = NULL;
char* token; char* token;
char* platform = NULL; const char* platform = NULL;
char* saveptr; char* saveptr;
size_t len = 0; size_t len = 0;
ssize_t nread; ssize_t nread;
bool ans;
char asic_file[512]; char asic_file[512];
char* str_num_asic; char* str_num_asic;
int num_asic = 1; int num_asic = 1;
const char* machine_config_file = get_machine_config_file();
fp = fopen(machine_config_file, "r"); platform = get_platform();
if (fp == NULL) {
fprintf(stderr, "Failed to open %s\n", machine_config_file);
exit(EXIT_FAILURE);
}
while ((nread = getline(&line, &len, fp)) != -1) {
if ((strstr(line, "onie_platform") != NULL) ||
(strstr(line, "aboot_platform") != NULL)) {
token = strtok_r(line, "=", &saveptr);
platform = strtok_r(NULL, "=", &saveptr);
strip_trailing_newline(platform);
break;
}
}
fclose(fp);
if(platform != NULL) { if(platform != NULL) {
snprintf(asic_file, 512, get_asic_conf_format(), platform); snprintf(asic_file, 512, get_asic_conf_format(), platform);
fp = fopen(asic_file, "r"); fp = fopen(asic_file, "r");
@ -582,6 +745,236 @@ int get_num_of_asic() {
} }
/**
* Retrieves the platform information.
*
* This function reads the platform information from a JSON file and returns it as a JSON object.
* If the platform information has already been retrieved, it returns the cached value.
*
* @return The platform information as a JSON object, or NULL if it fails to retrieve or parse the information.
*/
const struct json_object* get_platform_info() {
if (is_initialized_pointer(platform_info)) {
if (is_valid_pointer(platform_info)) {
return platform_info;
} else {
return NULL;
}
}
char platform_file_path[PATH_MAX];
const char* platform = get_platform();
if (platform == NULL) {
set_invalid_pointer((void **)&platform_info);
return NULL;
}
snprintf(platform_file_path, sizeof(platform_file_path), get_platform_file_format(), platform);
FILE *fp = fopen(platform_file_path, "r");
if (fp == NULL) {
fprintf(stdout, "Failed to open %s\n", platform_file_path);
set_invalid_pointer((void **)&platform_info);
return NULL;
}
if (fseek(fp, 0, SEEK_END) != 0) {
fprintf(stdout, "Failed to seek to end of %s\n", platform_file_path);
fclose(fp);
exit(EXIT_FAILURE);
}
size_t fsize = ftell(fp);
if (fseek(fp, 0, SEEK_SET) != 0) {
fprintf(stdout, "Failed to seek to beginning of %s\n", platform_file_path);
fclose(fp);
exit(EXIT_FAILURE);
}
char *platform_json = malloc(fsize + 1);
if (platform_json == NULL) {
fprintf(stdout, "Failed to allocate memory for %s\n", platform_file_path);
fclose(fp);
exit(EXIT_FAILURE);
}
if (fread(platform_json, fsize, 1, fp) != 1) {
fprintf(stdout, "Failed to read %s\n", platform_file_path);
fclose(fp);
exit(EXIT_FAILURE);
}
fclose(fp);
platform_json[fsize] = '\0';
platform_info = json_tokener_parse(platform_json);
if (platform_info == NULL) {
fprintf(stderr, "Failed to parse %s\n", platform_file_path);
free(platform_json);
return NULL;
}
free(platform_json);
return platform_info;
}
/**
* Checks if the platform is a smart switch with an NPU (Network Processing Unit).
*
* @return true if the platform is a smart switch with an NPU, false otherwise.
*/
static bool is_smart_switch_npu() {
struct json_object *dpus;
const struct json_object *platform_info = get_platform_info();
if (platform_info == NULL) {
return false;
}
if (!json_object_object_get_ex(platform_info, "DPUS", &dpus)) {
return false;
}
return true;
}
/**
* Checks if the current platform is a smart switch with a DPU (Data Processing Unit).
*
* @return true if the platform is a smart switch with a DPU, false otherwise.
*/
static bool is_smart_switch_dpu() {
struct json_object *dpu;
const struct json_object *platform_info = get_platform_info();
if (platform_info == NULL) {
return false;
}
if (!json_object_object_get_ex(platform_info, "DPU", &dpu)) {
return false;
}
return true;
}
/**
* @brief Retrieves the number of DPUs (Data Processing Units).
*
* This function retrieves the number of DPUs by accessing the platform information
* and extracting the "DPUS" array from it. If the platform information is not available
* or the "DPUS" array does not exist, the function returns 0.
*
* @return The number of DPUs.
*/
static int get_num_of_dpu() {
struct json_object *dpus;
const struct json_object *platform_info = get_platform_info();
if (platform_info == NULL) {
return 0;
}
if (!json_object_object_get_ex(platform_info, "DPUS", &dpus)) {
return 0;
}
size_t num_dpu = json_object_array_length(dpus);
return num_dpu;
}
static int revise_smart_switch_dependency_chain(int num_unit_files,const char* unit_files[MAX_NUM_UNITS]) {
assert(num_unit_files < MAX_NUM_UNITS);
if (!smart_switch || num_unit_files <= 0) {
return 0;
}
// Check the midplane-network.service is in the target list
bool found_midplane_network_service = false;
for (int i = 0; i < num_unit_files; i++) {
if (strcmp(unit_files[i], "midplane-network.service") == 0) {
found_midplane_network_service = true;
break;
}
}
if (!found_midplane_network_service) {
fprintf(stderr, "midplane-network.service is required in the target list\n");
return -1;
}
// Set the midplane-network.service as before and required
const char* after_units[MAX_NUM_UNITS] = {NULL};
assert(MAX_NUM_UNITS > 3);
after_units[0] = "database.service";
if (smart_switch_npu) {
after_units[1] = "database@.service";
}
const char** unit = after_units;
static const char required_after_statements[] = "\nRequires=midplane-network.service\nAfter=midplane-network.service\n";
static const size_t required_after_statements_len = sizeof(required_after_statements) - 1;
static const char unit_tag[] = "[Unit]";
static const size_t unit_tag_len = sizeof(unit_tag) - 1;
while(*unit != NULL) {
char unit_path[PATH_MAX] = {0};
FILE *fp;
strcpy(unit_path, get_unit_file_prefix());
strcat(unit_path, *unit);
// Read all data from file
fp = fopen(unit_path, "r");
if (fp == NULL) {
fprintf(stderr, "Failed to open %s\n", unit_path);
return -1;
}
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
size_t len = fsize + required_after_statements_len + 1;
char *data = malloc(len);
if (data == NULL) {
fprintf(stderr, "Failed to allocate memory for %s\n", unit_path);
fclose(fp);
return -1;
}
if (fread(data, fsize, 1, fp) != 1) {
fprintf(stderr, "Failed to read %s\n", unit_path);
fclose(fp);
free(data);
return -1;
}
fclose(fp);
data[fsize] = '\0';
// Check if the unit file already has the required statements
if (strstr(data, required_after_statements) != NULL) {
free(data);
unit++;
continue;
}
// Append the required statements to the Unit section of the unit file
char *unit_section = strstr(data, unit_tag);
if (unit_section == NULL) {
fprintf(stderr, "Failed to find %s section in %s\n", unit_tag, unit_path);
free(data);
return -1;
}
unit_section += unit_tag_len;
memmove(unit_section + required_after_statements_len, unit_section, strlen(unit_section) + 1);
memcpy(unit_section, required_after_statements, required_after_statements_len);
// Write the data back to the unit file
fp = fopen(unit_path, "w");
if (fp == NULL) {
fprintf(stderr, "Failed to open %s\n", unit_path);
free(data);
return -1;
}
// Don't write the trailing '\0'
assert(len > 1);
if (fwrite(data, len - 1, 1, fp) != 1) {
fprintf(stderr, "Failed to write %s\n", unit_path);
fclose(fp);
free(data);
return -1;
}
fclose(fp);
free(data);
unit++;
}
return 0;
}
int ssg_main(int argc, char **argv) { int ssg_main(int argc, char **argv) {
char* unit_files[MAX_NUM_UNITS]; char* unit_files[MAX_NUM_UNITS];
char install_dir[PATH_MAX]; char install_dir[PATH_MAX];
@ -594,20 +987,38 @@ int ssg_main(int argc, char **argv) {
int num_targets; int num_targets;
int r; int r;
#ifdef _SSG_UNITTEST
clean_up_cache();
#endif
if (argc <= 1) { if (argc <= 1) {
fputs("Installation directory required as argument\n", stderr); fputs("Installation directory required as argument\n", stderr);
return 1; return 1;
} }
num_asics = get_num_of_asic(); num_asics = get_num_of_asic();
smart_switch_npu = is_smart_switch_npu();
smart_switch_dpu = is_smart_switch_dpu();
smart_switch = smart_switch_npu || smart_switch_dpu;
num_dpus = get_num_of_dpu();
strcpy(install_dir, argv[1]); strcpy(install_dir, argv[1]);
strcat(install_dir, "/"); strcat(install_dir, "/");
num_unit_files = get_unit_files(unit_files); num_unit_files = get_unit_files(unit_files);
// Revise dependency chain for smart switch
if (smart_switch) {
if (revise_smart_switch_dependency_chain(num_unit_files, (const char **)unit_files) != 0) {
return 1;
}
}
// For each unit file, get the installation targets and install the unit // For each unit file, get the installation targets and install the unit
for (int i = 0; i < num_unit_files; i++) { for (int i = 0; i < num_unit_files; i++) {
unit_instance = strdup(unit_files[i]); unit_instance = strdup(unit_files[i]);
if ((num_asics == 1) && strstr(unit_instance, "@") != NULL) { if ((num_asics == 1 &&
!is_multi_instance_service_for_dpu(unit_instance)) &&
strstr(unit_instance, "@") != NULL) {
prefix = strdup(strtok_r(unit_instance, "@", &saveptr)); prefix = strdup(strtok_r(unit_instance, "@", &saveptr));
suffix = strdup(strtok_r(NULL, "@", &saveptr)); suffix = strdup(strtok_r(NULL, "@", &saveptr));
@ -642,6 +1053,10 @@ int ssg_main(int argc, char **argv) {
} }
free(multi_instance_services); free(multi_instance_services);
if (is_valid_pointer(platform_info)) {
json_object_put(platform_info);
}
return 0; return 0;
} }

View File

@ -15,17 +15,19 @@ extern const char* UNIT_FILE_PREFIX;
extern const char* CONFIG_FILE; extern const char* CONFIG_FILE;
extern const char* MACHINE_CONF_FILE; extern const char* MACHINE_CONF_FILE;
extern const char* ASIC_CONF_FORMAT; extern const char* ASIC_CONF_FORMAT;
extern const char* PLATFORM_FILE_FORMAT;
extern const char* g_unit_file_prefix; extern const char* g_unit_file_prefix;
extern const char* g_config_file; extern const char* g_config_file;
extern const char* g_machine_config_file; extern const char* g_machine_config_file;
extern const char* g_asic_conf_format; extern const char* g_asic_conf_format;
extern const char* g_platform_file_format;
/* C-functions under test */ /* C-functions under test */
extern const char* get_unit_file_prefix(); extern const char* get_unit_file_prefix();
extern const char* get_config_file(); extern const char* get_config_file();
extern const char* get_machine_config_file(); extern const char* get_machine_config_file();
extern const char* get_asic_conf_format(); extern const char* get_asic_conf_format();
extern char* insert_instance_number(char* unit_file, int instance); extern char* insert_instance_number(char* unit_file, int instance, const char *instance_prefix);
extern int ssg_main(int argc, char** argv); extern int ssg_main(int argc, char** argv);
extern int get_num_of_asic(); extern int get_num_of_asic();
extern int get_install_targets(char* unit_file, char* targets[]); extern int get_install_targets(char* unit_file, char* targets[]);

View File

@ -0,0 +1,14 @@
[Unit]
Description=Database container
StartLimitIntervalSec=1200
StartLimitBurst=3
[Service]
User=root
ExecStartPre=/usr/local/bin/database.sh start
ExecStart=/usr/local/bin/database.sh wait
ExecStop=/usr/local/bin/database.sh stop
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=Database container
StartLimitIntervalSec=1200
StartLimitBurst=3
[Service]
User=root
ExecStartPre=/usr/local/bin/database.sh start %i
ExecStart=/usr/local/bin/database.sh wait %i
ExecStop=/usr/local/bin/database.sh stop %i
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,12 @@
[Unit]
Description=Initialize midplane network for smart switch
StartLimitIntervalSec=1200
StartLimitBurst=3
[Service]
ExecStart=/usr/bin/test.sh wait
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target