[multi-asic] Fixed systemd-sonic-generator for multi-asic (#7633)
* Fixed systemd-sonic-generator for multi-asic 1. In function insert_instance_number instance_string was malloced for 2 char size which was limiting the instance number value in instance_name to 1 digit. Fixed insert_instance_number to use asprintf to generate instancd_name for any number of instances. Added _GNU_SOURCE to CFLAGS for asprintf. 2. Fixed get_unit_files() to use calloc instead of malloc. Uninitialized memory was causing incorrect string mismatch error while comparing unit file name string. 3. Increased MAX_NUM_TARGETS and MAX_NUM_INSTALL_LINES values to 48 to handle more asic instances. 4. Added build UT support for systemd-sonic-generator: a. Refactor systemd-sonic-generator.c to be used with UT infra. b. Added UT infra to run build UT for systemd-sonic-generator c. Added functional level and program level UT class and test cases. * Resolved review comments. 1. Explicitly setting global pointers to NULL in definitions. 2. Added a space before ": public" in class definitions to align style with SONiC C++ files. * Merged strtok_r statements in single command. Signed-off-by: Anand Mehra <anamehra@cisco.com>
This commit is contained in:
parent
4ae6d3f5c9
commit
4f8dec9a6f
1
src/systemd-sonic-generator/.gitignore
vendored
1
src/systemd-sonic-generator/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
systemd-sonic-generator
|
||||
ssg_test
|
||||
debian/*
|
||||
!debian/changelog
|
||||
!debian/compat
|
||||
|
@ -1,5 +1,10 @@
|
||||
CC=gcc
|
||||
CFLAGS=-std=gnu99
|
||||
CFLAGS=-std=gnu99 -D_GNU_SOURCE
|
||||
|
||||
CPP=g++
|
||||
CPPFLAGS=-std=c++11 -D_GNU_SOURCE
|
||||
LFLAGS=-lpthread -lboost_filesystem -lboost_system
|
||||
GTEST=/usr/lib/x86_64-linux-gnu/libgtest.a
|
||||
|
||||
BINARY = systemd-sonic-generator
|
||||
MAIN_TARGET = $(BINARY)_1.0.0_$(CONFIGURED_ARCH).deb
|
||||
@ -20,3 +25,18 @@ install: $(BINARY)
|
||||
mkdir -p $(DESTDIR)/lib/systemd
|
||||
mkdir -p $(DESTDIR)/lib/systemd/system-generators
|
||||
cp ./systemd-sonic-generator $(DESTDIR)/lib/systemd/system-generators
|
||||
|
||||
.PHONY: test
|
||||
test: ssg_test
|
||||
./ssg_test
|
||||
|
||||
ssg_test: ssg-test.cc systemd-sonic-generator.o
|
||||
$(CPP) $(CPPFLAGS) -o $@ $^ $(GTEST) $(LFLAGS)
|
||||
|
||||
systemd-sonic-generator.o: systemd-sonic-generator.c
|
||||
$(CC) $(CFLAGS) -D_SSG_UNITTEST -o $@ -c $^
|
||||
|
||||
clean:
|
||||
rm -f ./systemd-sonic-generator
|
||||
rm -f ./systemd-sonic-generator.o
|
||||
rm -f ./ssg_test
|
||||
|
@ -8,7 +8,6 @@ PACKAGEVERSION = $(VERSION)
|
||||
dh $@
|
||||
|
||||
override_dh_auto_clean:
|
||||
override_dh_auto_test:
|
||||
override_dh_auto_build:
|
||||
override_dh_auto_install:
|
||||
make systemd-sonic-generator
|
||||
|
550
src/systemd-sonic-generator/ssg-test.cc
Normal file
550
src/systemd-sonic-generator/ssg-test.cc
Normal file
@ -0,0 +1,550 @@
|
||||
/*------------------------------------------------------------------
|
||||
* ssg-test.cc - systemd-sonic-generator Unit Test
|
||||
*
|
||||
* Initial: Apr 2021
|
||||
*
|
||||
* Copyright (c) 2021 by Cisco Systems, Inc.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include "systemd-sonic-generator.h"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace SSGTest {
|
||||
#define IS_MULTI_ASIC(x) ((x) > 1)
|
||||
#define IS_SINGLE_ASIC(x) ((x) <= 1)
|
||||
#define NUM_UNIT_FILES 6
|
||||
|
||||
/*
|
||||
* This test class uses following directory hierarchy for input and output
|
||||
* data for systemd-sonic-generator.
|
||||
*
|
||||
* tests/ssg-test/ --- Test data directory
|
||||
* |
|
||||
* |---generated_services.conf
|
||||
* |---machine.conf (systemd-sonic-generator fetch platform from here)
|
||||
* |---systemd/
|
||||
* | |--- *.service (Test unit files are copied from
|
||||
* | tests/testfiles/ to here)
|
||||
* |----test_platform/ (test platform)
|
||||
* | |---asic.conf
|
||||
* |
|
||||
* |----generator/ (Output Directory)
|
||||
*
|
||||
*/
|
||||
const std::string TEST_ROOT_DIR = "tests/ssg-test/";
|
||||
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_MACHINE_CONF = TEST_ROOT_DIR + "machine.conf";
|
||||
|
||||
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_OUTPUT_DIR = TEST_ROOT_DIR + "generator/";
|
||||
|
||||
const std::string TEST_CONFIG_FILE = TEST_ROOT_DIR + "generated_services.conf";
|
||||
|
||||
const std::string TEST_UNIT_FILES = "tests/testfiles/";
|
||||
|
||||
/* Input data for generated_services.conf */
|
||||
const std::vector<std::string> generated_services = {
|
||||
"multi_inst_a.service", /* Single instance of a multi asic service a */
|
||||
"multi_inst_a@.service", /* Multi-instance of a multi asic service a */
|
||||
"multi_inst_b@.service", /* Multi-instance of a multi asic service b */
|
||||
"single_inst.service", /* A single instance service */
|
||||
"test.service", /* A single instance test service
|
||||
to test dependency creation */
|
||||
"test.timer", /* A timer service */
|
||||
};
|
||||
|
||||
static std::mutex g_ssg_test_mutex;
|
||||
|
||||
class SystemdSonicGeneratorFixture : public testing::Test {
|
||||
protected:
|
||||
/* Save global variables before running tests */
|
||||
virtual void SetUp() {
|
||||
/* one test runs at a time */
|
||||
g_ssg_test_mutex.lock();
|
||||
|
||||
unit_file_prefix_ = g_unit_file_prefix;
|
||||
config_file_ = g_config_file;
|
||||
machine_config_file_ = g_machine_config_file;
|
||||
asic_conf_format_ = g_asic_conf_format;
|
||||
}
|
||||
|
||||
/* Restore global vars */
|
||||
virtual void TearDown() {
|
||||
g_unit_file_prefix = unit_file_prefix_;
|
||||
g_config_file = config_file_;
|
||||
g_machine_config_file = machine_config_file_;
|
||||
g_asic_conf_format = asic_conf_format_;
|
||||
|
||||
g_ssg_test_mutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
const char* unit_file_prefix_;
|
||||
const char* config_file_;
|
||||
const char* machine_config_file_;
|
||||
const char* asic_conf_format_;
|
||||
};
|
||||
|
||||
/*
|
||||
* class SsgFunctionTest
|
||||
* Implements functions to execute functional level tests.
|
||||
*/
|
||||
class SsgFunctionTest : public SystemdSonicGeneratorFixture {
|
||||
protected:
|
||||
/* This function generates the generated_services.conf file */
|
||||
void generate_generated_services_conf() {
|
||||
FILE* fp = fopen(TEST_CONFIG_FILE.c_str(), "w");
|
||||
ASSERT_NE(fp, nullptr);
|
||||
for (std::string str : generated_services) {
|
||||
fputs(str.c_str(), fp);
|
||||
fputs("\n", fp);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/* copy files from src_dir to dest_dir */
|
||||
void copyfiles(const char* src_dir, const char* dest_dir) {
|
||||
// Iterate through the source directory
|
||||
for (fs::directory_iterator file(src_dir);
|
||||
file != fs::directory_iterator(); ++file) {
|
||||
try {
|
||||
fs::path current(file->path());
|
||||
if(!fs::is_directory(current)) {
|
||||
/* Copy file */
|
||||
fs::copy_file( current, dest_dir / current.filename());
|
||||
}
|
||||
}
|
||||
catch(fs::filesystem_error const & e) {
|
||||
std:: cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save global variables before running tests */
|
||||
virtual void SetUp() {
|
||||
FILE* fp;
|
||||
SystemdSonicGeneratorFixture::SetUp();
|
||||
|
||||
/* Setup Input and Output directories and files */
|
||||
fs::path path{TEST_UNIT_FILE_PREFIX.c_str()};
|
||||
fs::create_directories(path);
|
||||
path = fs::path(TEST_OUTPUT_DIR.c_str());
|
||||
fs::create_directories(path);
|
||||
path = fs::path(TEST_PLATFORM_DIR.c_str());
|
||||
fs::create_directories(path);
|
||||
fp = fopen(TEST_MACHINE_CONF.c_str(), "w");
|
||||
ASSERT_NE(fp, nullptr);
|
||||
fputs("onie_platform=test_platform", fp);
|
||||
fclose(fp);
|
||||
generate_generated_services_conf();
|
||||
copyfiles(TEST_UNIT_FILES.c_str(), TEST_UNIT_FILE_PREFIX.c_str());
|
||||
}
|
||||
|
||||
/* Restore global vars */
|
||||
virtual void TearDown() {
|
||||
/* Delete ssg_test directory */
|
||||
EXPECT_TRUE(fs::exists(TEST_ROOT_DIR.c_str()));
|
||||
fs::path path{TEST_ROOT_DIR.c_str()};
|
||||
fs::remove_all(path);
|
||||
|
||||
SystemdSonicGeneratorFixture::TearDown();
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
/*
|
||||
* class SsgMainTest
|
||||
* Implements functions to test ssg_main routine.
|
||||
*/
|
||||
class SsgMainTest : public SsgFunctionTest {
|
||||
protected:
|
||||
/* Retrun true if string belongs to a multi instance service */
|
||||
bool is_multi_instance(const std::string str) {
|
||||
return (str.find("@") != std::string::npos) ? true : false;
|
||||
}
|
||||
|
||||
/* Returns true if it is a timer service */
|
||||
bool is_timer_service(const std::string str) {
|
||||
return (str.find(".timer") != std::string::npos) ? true : false;
|
||||
}
|
||||
|
||||
/* Find a string in a file */
|
||||
bool find_string_in_file(std::string str,
|
||||
std::string file_name,
|
||||
int num_asics) {
|
||||
bool found = false;
|
||||
std::string line;
|
||||
|
||||
std::ifstream file(TEST_UNIT_FILE_PREFIX + file_name);
|
||||
while (getline(file, line) && !found) {
|
||||
if (str == line) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/* This function validates if a given dependency list for an unit file
|
||||
* exists in the unit file as per expected_result. The items in the list
|
||||
* should exist if expected_result is true.
|
||||
*/
|
||||
void validate_output_dependency_list(std::vector<std::string> strs,
|
||||
std::string target,
|
||||
bool expected_result,
|
||||
int num_asics) {
|
||||
for (std::string str : strs) {
|
||||
bool finished = false;
|
||||
for (int i = 0 ; i < num_asics && !finished; ++i) {
|
||||
auto str_t = str;
|
||||
if (is_multi_instance(str)) {
|
||||
/* insert instance id in string */
|
||||
str_t = (boost::format{str} % i).str();
|
||||
} else {
|
||||
/* Run once for single instance */
|
||||
finished = true;
|
||||
}
|
||||
EXPECT_EQ(find_string_in_file(str_t, target, num_asics),
|
||||
expected_result)
|
||||
<< "Error validating " + str_t + " in " + target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function validates if unit file paths in the provided
|
||||
* list strs exists or not as per expected_result. The unit files
|
||||
* should exist if expected_result is true.
|
||||
*/
|
||||
void validate_output_unit_files(std::vector<std::string> strs,
|
||||
std::string target,
|
||||
bool expected_result,
|
||||
int num_asics) {
|
||||
for (std::string str : strs) {
|
||||
bool finished = false;
|
||||
for (int i = 0 ; i < num_asics && !finished; ++i) {
|
||||
auto str_t = str;
|
||||
if (is_multi_instance(str)) {
|
||||
/* insert instance id in string */
|
||||
str_t = (boost::format{str} % i).str();
|
||||
} else {
|
||||
/* Run once for single instance */
|
||||
finished = true;
|
||||
}
|
||||
fs::path path{TEST_OUTPUT_DIR + target + "/" + str_t};
|
||||
EXPECT_EQ(fs::exists(path), expected_result)
|
||||
<< "Failed validation: " << path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function validates the generated dependencies in a Unit File.
|
||||
*/
|
||||
void validate_depedency_in_unit_file(int num_asics) {
|
||||
std::string test_service = "test.service";
|
||||
|
||||
/* Validate Unit file dependency creation for multi instance
|
||||
* services. These entries should be present for multi asic
|
||||
* system but not present for single asic system.
|
||||
*/
|
||||
validate_output_dependency_list(multi_asic_dependency_list,
|
||||
test_service, IS_MULTI_ASIC(num_asics), num_asics);
|
||||
|
||||
/* 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,
|
||||
test_service, IS_SINGLE_ASIC(num_asics), num_asics);
|
||||
|
||||
/* Validate Unit file dependency creation for single instance
|
||||
* common services. These entries should not be present for multi
|
||||
* and single asic system.
|
||||
*/
|
||||
validate_output_dependency_list(common_dependency_list,
|
||||
test_service, true, num_asics);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function validates the list of generated Service Unit Files.
|
||||
*/
|
||||
void validate_service_file_generated_list(int num_asics) {
|
||||
std::string test_target = "multi-user.target.wants";
|
||||
validate_output_unit_files(multi_asic_service_list,
|
||||
test_target, IS_MULTI_ASIC(num_asics), num_asics);
|
||||
validate_output_unit_files(single_asic_service_list,
|
||||
test_target, IS_SINGLE_ASIC(num_asics), num_asics);
|
||||
validate_output_unit_files(common_service_list,
|
||||
test_target, true, num_asics);
|
||||
}
|
||||
|
||||
/* ssg_main test routine.
|
||||
* input: num_asics number of asics
|
||||
*/
|
||||
void ssg_main_test(int num_asics) {
|
||||
FILE* fp;
|
||||
std::vector<char*> argv_;
|
||||
std::vector<std::string> arguments = {
|
||||
"ssg_main",
|
||||
TEST_OUTPUT_DIR.c_str()
|
||||
};
|
||||
std::string num_asic_str = "NUM_ASIC=" + std::to_string(num_asics);
|
||||
|
||||
std::string unit_file_path = fs::current_path().string() + "/" +TEST_UNIT_FILE_PREFIX;
|
||||
g_unit_file_prefix = unit_file_path.c_str();
|
||||
g_config_file = TEST_CONFIG_FILE.c_str();
|
||||
g_machine_config_file = TEST_MACHINE_CONF.c_str();
|
||||
g_asic_conf_format = TEST_ASIC_CONF_FORMAT.c_str();
|
||||
|
||||
/* Set NUM_ASIC value in asic.conf */
|
||||
fp = fopen(TEST_ASIC_CONF.c_str(), "w");
|
||||
ASSERT_NE(fp, nullptr);
|
||||
fputs(num_asic_str.c_str(), fp);
|
||||
fclose(fp);
|
||||
|
||||
/* Create argv list for ssg_main. */
|
||||
for (const auto& arg : arguments) {
|
||||
argv_.push_back((char*)arg.data());
|
||||
}
|
||||
argv_.push_back(nullptr);
|
||||
|
||||
/* Call ssg_main */
|
||||
EXPECT_EQ(ssg_main(argv_.size(), argv_.data()), 0);
|
||||
|
||||
/* Validate systemd service template creation. */
|
||||
validate_service_file_generated_list(num_asics);
|
||||
|
||||
/* Validate Test Unit file for dependency creation. */
|
||||
validate_depedency_in_unit_file(num_asics);
|
||||
}
|
||||
|
||||
/* Save global variables before running tests */
|
||||
virtual void SetUp() {
|
||||
SsgFunctionTest::SetUp();
|
||||
}
|
||||
|
||||
/* Restore global vars */
|
||||
virtual void TearDown() {
|
||||
SsgFunctionTest::TearDown();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
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> common_service_list;
|
||||
static const std::vector<std::string> single_asic_dependency_list;
|
||||
static const std::vector<std::string> multi_asic_dependency_list;
|
||||
static const std::vector<std::string> common_dependency_list;
|
||||
};
|
||||
|
||||
/*
|
||||
* The following list defines the Service unit files symlinks generated by
|
||||
* Systemd sonic generator for single and multi asic systems. The test case
|
||||
* use these lists to check for presence/absence of unit files based on
|
||||
* num_asics value.
|
||||
*/
|
||||
|
||||
/* Systemd service Unit file list for single asic only system */
|
||||
const std::vector<std::string>
|
||||
SsgMainTest::single_asic_service_list = {
|
||||
"multi_inst_b.service",
|
||||
};
|
||||
|
||||
/* Systemd service Unit file list for multi asic only system.
|
||||
* %1% is formatter for boost::format API and replaced by asic num.
|
||||
*/
|
||||
const std::vector<std::string>
|
||||
SsgMainTest::multi_asic_service_list = {
|
||||
"multi_inst_a@%1%.service",
|
||||
"multi_inst_b@%1%.service",
|
||||
};
|
||||
|
||||
/* Common Systemd service Unit file list for single and multi asic system. */
|
||||
const std::vector<std::string>
|
||||
SsgMainTest::common_service_list = {
|
||||
"multi_inst_a.service",
|
||||
"single_inst.service",
|
||||
"test.service",
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* The following list defines the systemd dependencies in a unit file to be
|
||||
* varified for single and multi asic systems. Based on num_asics and type of
|
||||
* service listed as dependency in Unit file, systemd sonic generator modifies
|
||||
* the original unit file, if required, for multi asic system.
|
||||
* For example: if test.service file defines a dependency "After=multi_inst_a.service",
|
||||
* as multi_inst_a.service is a multi instance service,
|
||||
* for a system with 2 asics, systemd sonic generator shall modify
|
||||
* test.service to include following dependency strings:
|
||||
* "After=multi_inst_a@0.service"
|
||||
* After=multi_inst_a@1.service"
|
||||
*/
|
||||
|
||||
/* Systemd service Unit file dependency entries for Single asic system. */
|
||||
const std::vector<std::string>
|
||||
SsgMainTest::single_asic_dependency_list = {
|
||||
"After=multi_inst_a.service multi_inst_b.service",
|
||||
};
|
||||
|
||||
/* Systemd service Unit file dependency entries for multi asic system. */
|
||||
const std::vector<std::string>
|
||||
SsgMainTest::multi_asic_dependency_list = {
|
||||
"After=multi_inst_a@%1%.service",
|
||||
"After=multi_inst_b@%1%.service",
|
||||
};
|
||||
|
||||
/* Common Systemd service Unit file dependency entries for single and multi asic
|
||||
* systems.
|
||||
*/
|
||||
const std::vector<std::string>
|
||||
SsgMainTest::common_dependency_list = {
|
||||
"Before=single_inst.service",
|
||||
};
|
||||
|
||||
/* Test get functions for global vasr*/
|
||||
TEST_F(SystemdSonicGeneratorFixture, get_global_vars) {
|
||||
EXPECT_EQ(g_unit_file_prefix, nullptr);
|
||||
EXPECT_STREQ(get_unit_file_prefix(), UNIT_FILE_PREFIX);
|
||||
g_unit_file_prefix = TEST_UNIT_FILE_PREFIX.c_str();
|
||||
EXPECT_STREQ(get_unit_file_prefix(), TEST_UNIT_FILE_PREFIX.c_str());
|
||||
|
||||
EXPECT_EQ(g_config_file, nullptr);
|
||||
EXPECT_STREQ(get_config_file(), CONFIG_FILE);
|
||||
g_config_file = TEST_CONFIG_FILE.c_str();
|
||||
EXPECT_STREQ(get_config_file(), TEST_CONFIG_FILE.c_str());
|
||||
|
||||
EXPECT_EQ(g_machine_config_file, nullptr);
|
||||
EXPECT_STREQ(get_machine_config_file(), MACHINE_CONF_FILE);
|
||||
g_machine_config_file = TEST_MACHINE_CONF.c_str();
|
||||
EXPECT_STREQ(get_machine_config_file(), TEST_MACHINE_CONF.c_str());
|
||||
|
||||
EXPECT_EQ(g_asic_conf_format, nullptr);
|
||||
EXPECT_STREQ(get_asic_conf_format(), ASIC_CONF_FORMAT);
|
||||
g_asic_conf_format = TEST_ASIC_CONF_FORMAT.c_str();
|
||||
EXPECT_STREQ(get_asic_conf_format(), TEST_ASIC_CONF_FORMAT.c_str());
|
||||
}
|
||||
|
||||
TEST_F(SystemdSonicGeneratorFixture, global_vars) {
|
||||
EXPECT_EQ(g_unit_file_prefix, nullptr);
|
||||
EXPECT_STREQ(get_unit_file_prefix(), UNIT_FILE_PREFIX);
|
||||
|
||||
EXPECT_EQ(g_config_file, nullptr);
|
||||
EXPECT_STREQ(get_config_file(), CONFIG_FILE);
|
||||
|
||||
EXPECT_EQ(g_machine_config_file, nullptr);
|
||||
EXPECT_STREQ(get_machine_config_file(), MACHINE_CONF_FILE);
|
||||
}
|
||||
|
||||
/* TEST machine/unit/config if file is missing */
|
||||
TEST_F(SsgFunctionTest, missing_file) {
|
||||
EXPECT_TRUE(fs::exists(TEST_MACHINE_CONF.c_str()));
|
||||
EXPECT_TRUE(fs::exists(TEST_UNIT_FILE_PREFIX.c_str()));
|
||||
EXPECT_TRUE(fs::exists(TEST_OUTPUT_DIR.c_str()));
|
||||
EXPECT_TRUE(fs::exists(TEST_PLATFORM_DIR.c_str()));
|
||||
}
|
||||
|
||||
/* TEST insert_instance_number() */
|
||||
TEST_F(SsgFunctionTest, insert_instance_number) {
|
||||
char input[] = "test@.service";
|
||||
for (int i = 0; i <= 100; ++i) {
|
||||
std::string out = "test@" + std::to_string(i) + ".service";
|
||||
char* ret = insert_instance_number(input, i);
|
||||
ASSERT_NE(ret, nullptr);
|
||||
EXPECT_STREQ(ret, out.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/* TEST get_num_of_asic() */
|
||||
TEST_F(SsgFunctionTest, get_num_of_asic) {
|
||||
FILE* fp;
|
||||
|
||||
g_machine_config_file = TEST_MACHINE_CONF.c_str();
|
||||
g_asic_conf_format = TEST_ASIC_CONF_FORMAT.c_str();
|
||||
|
||||
fp = fopen(TEST_ASIC_CONF.c_str(), "w");
|
||||
ASSERT_NE(fp, nullptr);
|
||||
fputs("NUM_ASIC=1", fp);
|
||||
fclose(fp);
|
||||
EXPECT_EQ(get_num_of_asic(), 1);
|
||||
|
||||
fp = fopen(TEST_ASIC_CONF.c_str(), "w");
|
||||
ASSERT_NE(fp, nullptr);
|
||||
fputs("NUM_ASIC=10", fp);
|
||||
fclose(fp);
|
||||
EXPECT_EQ(get_num_of_asic(), 10);
|
||||
|
||||
fp = fopen(TEST_ASIC_CONF.c_str(), "w");
|
||||
ASSERT_NE(fp, nullptr);
|
||||
fputs("NUM_ASIC=40", fp);
|
||||
fclose(fp);
|
||||
EXPECT_EQ(get_num_of_asic(), 40);
|
||||
}
|
||||
|
||||
/* TEST get_unit_files()*/
|
||||
TEST_F(SsgFunctionTest, get_unit_files) {
|
||||
g_unit_file_prefix = TEST_UNIT_FILE_PREFIX.c_str();
|
||||
g_config_file = TEST_CONFIG_FILE.c_str();
|
||||
char* unit_files[NUM_UNIT_FILES];
|
||||
int num_unit_files = get_unit_files(unit_files);
|
||||
EXPECT_EQ(num_unit_files, NUM_UNIT_FILES);
|
||||
for (std::string service : generated_services) {
|
||||
bool found = false;
|
||||
for (auto& unit_file : unit_files) {
|
||||
if(unit_file == service) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found) << "unit file not found: " << service;
|
||||
}
|
||||
}
|
||||
|
||||
/* TEST ssg_main() argv error */
|
||||
TEST_F(SsgMainTest, ssg_main_argv) {
|
||||
FILE* fp;
|
||||
std::vector<char*> argv_;
|
||||
std::vector<std::string> arguments = {
|
||||
"ssg_main",
|
||||
};
|
||||
|
||||
/* Create argv list for ssg_main. */
|
||||
for (const auto& arg : arguments) {
|
||||
argv_.push_back((char*)arg.data());
|
||||
}
|
||||
|
||||
/* Call ssg_main */
|
||||
EXPECT_EQ(ssg_main(argv_.size(), argv_.data()), 1);
|
||||
}
|
||||
|
||||
/* TEST ssg_main() single asic */
|
||||
TEST_F(SsgMainTest, ssg_main_single_npu) {
|
||||
ssg_main_test(1);
|
||||
}
|
||||
|
||||
/* TEST ssg_main() multi(10) asic */
|
||||
TEST_F(SsgMainTest, ssg_main_10_npu) {
|
||||
ssg_main_test(10);
|
||||
}
|
||||
|
||||
/* TEST ssg_main() multi(40) asic */
|
||||
TEST_F(SsgMainTest, ssg_main_40_npu) {
|
||||
ssg_main_test(40);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -9,14 +9,36 @@
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#define MAX_NUM_TARGETS 15
|
||||
#define MAX_NUM_INSTALL_LINES 15
|
||||
#define MAX_NUM_TARGETS 48
|
||||
#define MAX_NUM_INSTALL_LINES 48
|
||||
#define MAX_NUM_UNITS 128
|
||||
#define MAX_BUF_SIZE 512
|
||||
|
||||
static const char* UNIT_FILE_PREFIX = "/usr/lib/systemd/system/";
|
||||
static const char* CONFIG_FILE = "/etc/sonic/generated_services.conf";
|
||||
static const char* MACHINE_CONF_FILE = "/host/machine.conf";
|
||||
const char* UNIT_FILE_PREFIX = "/usr/lib/systemd/system/";
|
||||
const char* CONFIG_FILE = "/etc/sonic/generated_services.conf";
|
||||
const char* MACHINE_CONF_FILE = "/host/machine.conf";
|
||||
const char* ASIC_CONF_FORMAT = "/usr/share/sonic/device/%s/asic.conf";
|
||||
|
||||
const char* g_unit_file_prefix = NULL;
|
||||
const char* get_unit_file_prefix() {
|
||||
return (g_unit_file_prefix) ? g_unit_file_prefix : UNIT_FILE_PREFIX;
|
||||
}
|
||||
|
||||
const char* g_config_file = NULL;
|
||||
const char* get_config_file() {
|
||||
return (g_config_file) ? g_config_file : CONFIG_FILE;
|
||||
}
|
||||
|
||||
const char* g_machine_config_file = NULL;
|
||||
const char* get_machine_config_file() {
|
||||
return (g_machine_config_file) ? g_machine_config_file : MACHINE_CONF_FILE;
|
||||
}
|
||||
|
||||
const char* g_asic_conf_format = NULL;
|
||||
const char* get_asic_conf_format() {
|
||||
return (g_asic_conf_format) ? g_asic_conf_format : ASIC_CONF_FORMAT;
|
||||
}
|
||||
|
||||
static int num_asics;
|
||||
static char** multi_instance_services;
|
||||
static int num_multi_inst;
|
||||
@ -128,7 +150,7 @@ static int get_install_targets_from_line(char* target_string, char* install_type
|
||||
strcat(final_target, install_type);
|
||||
|
||||
free(target);
|
||||
|
||||
|
||||
targets[num_targets + existing_targets] = strdup(final_target);
|
||||
num_targets++;
|
||||
}
|
||||
@ -152,12 +174,12 @@ static void replace_multi_inst_dep(char *src) {
|
||||
ssize_t nread;
|
||||
bool section_done = false;
|
||||
char tmp_file_path[PATH_MAX];
|
||||
|
||||
|
||||
/* Assumes that the service files has 3 sections,
|
||||
* in the order: Unit, Service and Install.
|
||||
* Assumes that the timer file has 3 sectiosn,
|
||||
* Assumes that the timer file has 3 sections,
|
||||
* in the order: Unit, Timer and Install.
|
||||
* Read service dependency from Unit and Install
|
||||
* Read service dependency from Unit and Install
|
||||
* sections, replace if dependent on multi instance
|
||||
* service.
|
||||
*/
|
||||
@ -166,7 +188,7 @@ static void replace_multi_inst_dep(char *src) {
|
||||
fp_tmp = fopen(tmp_file_path, "w");
|
||||
|
||||
while ((nread = getline(&line, &len, fp_src)) != -1 ) {
|
||||
if ((strstr(line, "[Service]") != NULL) ||
|
||||
if ((strstr(line, "[Service]") != NULL) ||
|
||||
(strstr(line, "[Timer]") != NULL)) {
|
||||
section_done = true;
|
||||
fputs(line,fp_tmp);
|
||||
@ -188,7 +210,7 @@ static void replace_multi_inst_dep(char *src) {
|
||||
} else {
|
||||
service_name = strdup(word);
|
||||
service_name = strtok_r(service_name, ".", &save_ptr2);
|
||||
type = strtok_r(NULL, " ", &save_ptr2);
|
||||
type = strtok_r(NULL, "\n", &save_ptr2);
|
||||
if (is_multi_instance_service(word)) {
|
||||
for(i = 0; i < num_asics; i++) {
|
||||
snprintf(buf, MAX_BUF_SIZE, "%s=%s@%d.%s\n",
|
||||
@ -215,7 +237,7 @@ static void replace_multi_inst_dep(char *src) {
|
||||
rename(tmp_file_path, src);
|
||||
}
|
||||
|
||||
static int get_install_targets(char* unit_file, char* targets[]) {
|
||||
int get_install_targets(char* unit_file, char* targets[]) {
|
||||
/***
|
||||
Returns install targets for a unit file
|
||||
|
||||
@ -234,7 +256,7 @@ static int get_install_targets(char* unit_file, char* targets[]) {
|
||||
char *instance_name;
|
||||
char *dot_ptr;
|
||||
|
||||
strcpy(file_path, UNIT_FILE_PREFIX);
|
||||
strcpy(file_path, get_unit_file_prefix());
|
||||
strcat(file_path, unit_file);
|
||||
|
||||
instance_name = strdup(unit_file);
|
||||
@ -280,7 +302,7 @@ static int get_install_targets(char* unit_file, char* targets[]) {
|
||||
}
|
||||
|
||||
|
||||
static int get_unit_files(char* unit_files[]) {
|
||||
int get_unit_files(char* unit_files[]) {
|
||||
/***
|
||||
Reads a list of unit files to be installed from /etc/sonic/generated_services.conf
|
||||
***/
|
||||
@ -289,18 +311,19 @@ static int get_unit_files(char* unit_files[]) {
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
char *pos;
|
||||
const char* config_file = get_config_file();
|
||||
|
||||
fp = fopen(CONFIG_FILE, "r");
|
||||
fp = fopen(config_file, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Failed to open %s\n", CONFIG_FILE);
|
||||
fprintf(stderr, "Failed to open %s\n", config_file);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int num_unit_files = 0;
|
||||
num_multi_inst = 0;
|
||||
|
||||
multi_instance_services = malloc(MAX_NUM_UNITS * sizeof(char *));
|
||||
multi_instance_services = calloc(MAX_NUM_UNITS, sizeof(char *));
|
||||
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
if (num_unit_files >= MAX_NUM_UNITS) {
|
||||
@ -312,7 +335,7 @@ static int get_unit_files(char* unit_files[]) {
|
||||
/* Get the multi-instance services */
|
||||
pos = strchr(line, '@');
|
||||
if (pos != NULL) {
|
||||
multi_instance_services[num_multi_inst] = malloc(strlen(line)*sizeof(char));
|
||||
multi_instance_services[num_multi_inst] = calloc(strlen(line), sizeof(char));
|
||||
strncpy(multi_instance_services[num_multi_inst], line, pos-line);
|
||||
num_multi_inst++;
|
||||
}
|
||||
@ -334,40 +357,32 @@ static int get_unit_files(char* unit_files[]) {
|
||||
}
|
||||
|
||||
|
||||
static char* insert_instance_number(char* unit_file, int instance) {
|
||||
char* insert_instance_number(char* unit_file, int instance) {
|
||||
/***
|
||||
Adds an instance number to a systemd template name
|
||||
|
||||
E.g. given unit_file='example@.service', instance=3,
|
||||
returns a pointer to 'example@1.service'
|
||||
returns a pointer to 'example@3.service'
|
||||
***/
|
||||
char* prefix;
|
||||
char* suffix;
|
||||
char* instance_string;
|
||||
char* instance_name;
|
||||
char* temp_unit_file;
|
||||
|
||||
instance_string = malloc(2 * sizeof(char));
|
||||
snprintf(instance_string, 2, "%d", instance);
|
||||
|
||||
instance_name = malloc(strlen(unit_file) + 2);
|
||||
|
||||
if (instance_name == NULL) {
|
||||
fprintf(stderr, "Error creating instance %d of %s\n", instance, unit_file);
|
||||
int ret;
|
||||
int prefix_len;
|
||||
const char *suffix = strchr(unit_file, '@');
|
||||
if (!suffix) {
|
||||
fprintf(stderr, "Invalid unit file %s for instance %d\n", unit_file, instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
temp_unit_file = strdup(unit_file);
|
||||
prefix = strtok(temp_unit_file, "@");
|
||||
suffix = strtok(NULL, "@");
|
||||
|
||||
strcpy(instance_name, prefix);
|
||||
strcat(instance_name, "@");
|
||||
strcat(instance_name, instance_string);
|
||||
strcat(instance_name, suffix);
|
||||
|
||||
free(instance_string);
|
||||
free(temp_unit_file);
|
||||
/***
|
||||
suffix is "@.service", set suffix=".service"
|
||||
prefix_len is length of "example@"
|
||||
***/
|
||||
prefix_len = ++suffix - unit_file;
|
||||
ret = asprintf(&instance_name, "%.*s%d%s", prefix_len, unit_file, instance, suffix);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "Error creating instance %d of %s\n", instance, unit_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return instance_name;
|
||||
}
|
||||
@ -381,7 +396,7 @@ static int create_symlink(char* unit, char* target, char* install_dir, int insta
|
||||
char* unit_instance;
|
||||
int r;
|
||||
|
||||
strcpy(src_path, UNIT_FILE_PREFIX);
|
||||
strcpy(src_path, get_unit_file_prefix());
|
||||
strcat(src_path, unit);
|
||||
|
||||
if (instance < 0) {
|
||||
@ -462,7 +477,7 @@ static int install_unit_file(char* unit_file, char* target, char* install_dir) {
|
||||
|
||||
assert(unit_file);
|
||||
assert(target);
|
||||
|
||||
|
||||
|
||||
if ((num_asics > 1) && strstr(unit_file, "@") != NULL) {
|
||||
|
||||
@ -476,16 +491,16 @@ static int install_unit_file(char* unit_file, char* target, char* install_dir) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
free(target_instance);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -493,7 +508,7 @@ static int install_unit_file(char* unit_file, char* target, char* install_dir) {
|
||||
}
|
||||
|
||||
|
||||
static int get_num_of_asic() {
|
||||
int get_num_of_asic() {
|
||||
/***
|
||||
Determines if the current platform is single or multi-ASIC
|
||||
***/
|
||||
@ -507,11 +522,12 @@ static int get_num_of_asic() {
|
||||
char asic_file[512];
|
||||
char* str_num_asic;
|
||||
int num_asic = 1;
|
||||
const char* machine_config_file = get_machine_config_file();
|
||||
|
||||
fp = fopen(MACHINE_CONF_FILE, "r");
|
||||
fp = fopen(machine_config_file, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Failed to open %s\n", MACHINE_CONF_FILE);
|
||||
fprintf(stderr, "Failed to open %s\n", machine_config_file);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -524,10 +540,9 @@ static int get_num_of_asic() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
if(platform != NULL) {
|
||||
snprintf(asic_file, 512, "/usr/share/sonic/device/%s/asic.conf", platform);
|
||||
snprintf(asic_file, 512, get_asic_conf_format(), platform);
|
||||
fp = fopen(asic_file, "r");
|
||||
if (fp != NULL) {
|
||||
while ((nread = getline(&line, &len, fp)) != -1) {
|
||||
@ -549,8 +564,7 @@ static int get_num_of_asic() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int ssg_main(int argc, char **argv) {
|
||||
char* unit_files[MAX_NUM_UNITS];
|
||||
char install_dir[PATH_MAX];
|
||||
char* targets[MAX_NUM_TARGETS];
|
||||
@ -567,10 +581,8 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
num_asics = get_num_of_asic();
|
||||
|
||||
strcpy(install_dir, argv[1]);
|
||||
strcat(install_dir, "/");
|
||||
|
||||
num_unit_files = get_unit_files(unit_files);
|
||||
|
||||
// For each unit file, get the installation targets and install the unit
|
||||
@ -610,3 +622,10 @@ int main(int argc, char **argv) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifndef _SSG_UNITTEST
|
||||
int main(int argc, char **argv) {
|
||||
return ssg_main(argc, argv);
|
||||
}
|
||||
#endif
|
||||
|
35
src/systemd-sonic-generator/systemd-sonic-generator.h
Normal file
35
src/systemd-sonic-generator/systemd-sonic-generator.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*------------------------------------------------------------------
|
||||
* systemd-sonic-generator.h - Header file
|
||||
*
|
||||
* Initial: Apr 2021
|
||||
*
|
||||
* Copyright (c) 2021 by Cisco Systems, Inc.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* expose global vars for testing purpose */
|
||||
extern const char* UNIT_FILE_PREFIX;
|
||||
extern const char* CONFIG_FILE;
|
||||
extern const char* MACHINE_CONF_FILE;
|
||||
extern const char* ASIC_CONF_FORMAT;
|
||||
extern const char* g_unit_file_prefix;
|
||||
extern const char* g_config_file;
|
||||
extern const char* g_machine_config_file;
|
||||
extern const char* g_asic_conf_format;
|
||||
|
||||
/* C-functions under test */
|
||||
extern const char* get_unit_file_prefix();
|
||||
extern const char* get_config_file();
|
||||
extern const char* get_machine_config_file();
|
||||
extern const char* get_asic_conf_format();
|
||||
extern char* insert_instance_number(char* unit_file, int instance);
|
||||
extern int ssg_main(int argc, char** argv);
|
||||
extern int get_num_of_asic();
|
||||
extern int get_install_targets(char* unit_file, char* targets[]);
|
||||
extern int get_unit_files(char* unit_files[]);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Multi Instance A Test service
|
||||
StartLimitIntervalSec=1200
|
||||
StartLimitBurst=3
|
||||
[Service]
|
||||
User=root
|
||||
ExecStop=/usr/bin/test.sh stop
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Multi INstance A test Service
|
||||
|
||||
After=multi_inst_a.service
|
||||
StartLimitIntervalSec=1200
|
||||
StartLimitBurst=3
|
||||
[Service]
|
||||
User=root
|
||||
ExecStop=/usr/bin/test.sh stop %i
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Multi instance b Test service
|
||||
|
||||
Requires=multi_inst_a.service
|
||||
StartLimitIntervalSec=1200
|
||||
StartLimitBurst=3
|
||||
[Service]
|
||||
User=root
|
||||
ExecStop=/usr/bin/test.sh stop
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Multi instance b Test service
|
||||
Requires=multi_inst_a@%i.service
|
||||
StartLimitIntervalSec=1200
|
||||
StartLimitBurst=3
|
||||
[Service]
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/test.sh wait %i
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Platform monitor container
|
||||
Requires=multi_inst_a.service
|
||||
StartLimitIntervalSec=1200
|
||||
StartLimitBurst=3
|
||||
[Service]
|
||||
ExecStart=/usr/bin/test.sh wait
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
10
src/systemd-sonic-generator/tests/testfiles/test.service
Normal file
10
src/systemd-sonic-generator/tests/testfiles/test.service
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Multi ASIC Test service
|
||||
After=multi_inst_a.service multi_inst_b.service
|
||||
Before=single_inst.service
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/bin/test.sh start
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
10
src/systemd-sonic-generator/tests/testfiles/test.timer
Normal file
10
src/systemd-sonic-generator/tests/testfiles/test.timer
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Test Timer service
|
||||
After=multi_inst_b.service
|
||||
[Timer]
|
||||
OnUnitActiveSec=0 sec
|
||||
OnBootSec=3min 30 sec
|
||||
Unit=snmp.service
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
WantedBy=multi_inst_b.service
|
Loading…
Reference in New Issue
Block a user