From e6bc0f3b5dabcb3960f5a4f5e6bfdb14c7059774 Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Mon, 26 Feb 2024 00:58:22 +0800 Subject: [PATCH] Refactor ss service Signed-off-by: Ze Gan --- .../build_templates/sonic_debian_extension.j2 | 20 ++ .../midplane-network/bridge-midplane.netdev | 5 + .../midplane-network/bridge-midplane.network | 7 + .../midplane-network-dpu.network | 5 + .../midplane-network-dpu.service | 15 + .../midplane-network-npu.network | 5 + .../midplane-network-npu.service | 14 + src/systemd-sonic-generator/ssg-test.cc | 124 +++++-- .../systemd-sonic-generator.c | 311 +++++++++++++----- .../systemd-sonic-generator.h | 4 +- .../tests/testfiles/bridge-midplane.netdev | 1 + .../tests/testfiles/bridge-midplane.network | 1 + .../testfiles/midplane-network-dpu.network | 1 + .../testfiles/midplane-network-dpu.service | 1 + .../testfiles/midplane-network-npu.network | 1 + .../testfiles/midplane-network-npu.service | 1 + .../tests/testfiles/midplane-network.service | 12 - 17 files changed, 392 insertions(+), 136 deletions(-) create mode 100644 files/image_config/midplane-network/bridge-midplane.netdev create mode 100644 files/image_config/midplane-network/bridge-midplane.network create mode 100644 files/image_config/midplane-network/midplane-network-dpu.network create mode 100644 files/image_config/midplane-network/midplane-network-dpu.service create mode 100644 files/image_config/midplane-network/midplane-network-npu.network create mode 100644 files/image_config/midplane-network/midplane-network-npu.service create mode 120000 src/systemd-sonic-generator/tests/testfiles/bridge-midplane.netdev create mode 120000 src/systemd-sonic-generator/tests/testfiles/bridge-midplane.network create mode 120000 src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.network create mode 120000 src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.service create mode 120000 src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.network create mode 120000 src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.service delete mode 100644 src/systemd-sonic-generator/tests/testfiles/midplane-network.service diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index ed52539e2a..12e0695e91 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -47,6 +47,7 @@ DOCKER_CTL_SCRIPT="$DOCKER_CTL_DIR/docker.sh" FILESYSTEM_ROOT_USR="$FILESYSTEM_ROOT/usr" FILESYSTEM_ROOT_USR_LIB="$FILESYSTEM_ROOT/usr/lib/" FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM="$FILESYSTEM_ROOT_USR_LIB/systemd/system" +FILESYSTEM_ROOT_USR_LIB_SYSTEMD_NETWORK="$FILESYSTEM_ROOT_USR_LIB/systemd/network" FILESYSTEM_ROOT_USR_SHARE="$FILESYSTEM_ROOT_USR/share" FILESYSTEM_ROOT_USR_SHARE_SONIC="$FILESYSTEM_ROOT_USR_SHARE/sonic" FILESYSTEM_ROOT_USR_SHARE_SONIC_SCRIPTS="$FILESYSTEM_ROOT_USR_SHARE_SONIC/scripts" @@ -629,6 +630,25 @@ sudo cp $IMAGE_CONFIGS/config-chassisdb/config-chassisdb $FILESYSTEM_ROOT/usr/bi echo "config-chassisdb.service" | sudo tee -a $GENERATED_SERVICE_FILE sudo LANG=C chroot $FILESYSTEM_ROOT systemctl enable config-chassisdb.service +# Copy midplane network service file for smart switch +sudo cp $IMAGE_CONFIGS/midplane-network/bridge-midplane.netdev $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_NETWORK/bridge-midplane.netdev +sudo cp $IMAGE_CONFIGS/midplane-network/bridge-midplane.network $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_NETWORK/bridge-midplane.network +sudo cp $IMAGE_CONFIGS/midplane-network/midplane-network-npu.network $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_NETWORK/midplane-network-npu.network +sudo cp $IMAGE_CONFIGS/midplane-network/midplane-network-dpu.network $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_NETWORK/midplane-network-dpu.network +sudo cp $IMAGE_CONFIGS/midplane-network/midplane-network-npu.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM/midplane-network-npu.service +sudo cp $IMAGE_CONFIGS/midplane-network/midplane-network-dpu.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM/midplane-network-dpu.service + +# Disable smart switch unit by default, these units will be controlled by systemd-sonic-generator +sudo ln -s /dev/null /etc/systemd/network/bridge-midplane.netdev +sudo ln -s /dev/null /etc/systemd/network/bridge-midplane.network +sudo ln -s /dev/null /etc/systemd/network/midplane-network-npu.network +sudo ln -s /dev/null /etc/systemd/network/midplane-network-dpu.network +echo "midplane-network-npu.service" | sudo tee -a $GENERATED_SERVICE_FILE +sudo LANG=C chroot $FILESYSTEM_ROOT systemctl disable midplane-network-npu.service +echo "midplane-network-dpu.service" | sudo tee -a $GENERATED_SERVICE_FILE +sudo LANG=C chroot $FILESYSTEM_ROOT systemctl disable midplane-network-dpu.service + + # Copy backend-acl script and service file sudo cp $IMAGE_CONFIGS/backend_acl/backend-acl.service $FILESYSTEM_ROOT_USR_LIB_SYSTEMD_SYSTEM/backend-acl.service sudo cp $IMAGE_CONFIGS/backend_acl/backend_acl.py $FILESYSTEM_ROOT/usr/bin/backend_acl.py diff --git a/files/image_config/midplane-network/bridge-midplane.netdev b/files/image_config/midplane-network/bridge-midplane.netdev new file mode 100644 index 0000000000..829d659893 --- /dev/null +++ b/files/image_config/midplane-network/bridge-midplane.netdev @@ -0,0 +1,5 @@ +# Bridge interface for midplane network + +[NetDev] +Name=bridge-midplane +Kind=bridge diff --git a/files/image_config/midplane-network/bridge-midplane.network b/files/image_config/midplane-network/bridge-midplane.network new file mode 100644 index 0000000000..1ff42d065a --- /dev/null +++ b/files/image_config/midplane-network/bridge-midplane.network @@ -0,0 +1,7 @@ +# Midplane network for NPU + +[Match] +Name=bridge-midplane + +[Network] +Address=169.254.200.254/24 diff --git a/files/image_config/midplane-network/midplane-network-dpu.network b/files/image_config/midplane-network/midplane-network-dpu.network new file mode 100644 index 0000000000..bb4c58ed9a --- /dev/null +++ b/files/image_config/midplane-network/midplane-network-dpu.network @@ -0,0 +1,5 @@ +[Match] +Name=eth0-midplane + +[Network] +DHCP=yes diff --git a/files/image_config/midplane-network/midplane-network-dpu.service b/files/image_config/midplane-network/midplane-network-dpu.service new file mode 100644 index 0000000000..5750850b73 --- /dev/null +++ b/files/image_config/midplane-network/midplane-network-dpu.service @@ -0,0 +1,15 @@ +# Oneshot midplane network service + +[Unit] +Description=Midplane network service +Requires=systemd-networkd.service +After=systemd-networkd.service +Before=database.service + +[Service] +Type=oneshot +User=root +ExecStart=/usr/lib/systemd/systemd-networkd-wait-online -i eth0-midplane --timeout=600 + +[Install] +WantedBy=multi-user.target diff --git a/files/image_config/midplane-network/midplane-network-npu.network b/files/image_config/midplane-network/midplane-network-npu.network new file mode 100644 index 0000000000..8cc4a22649 --- /dev/null +++ b/files/image_config/midplane-network/midplane-network-npu.network @@ -0,0 +1,5 @@ +[Match] +Name=dpu* + +[Network] +Bridge=bridge-midplane diff --git a/files/image_config/midplane-network/midplane-network-npu.service b/files/image_config/midplane-network/midplane-network-npu.service new file mode 100644 index 0000000000..785f048ac7 --- /dev/null +++ b/files/image_config/midplane-network/midplane-network-npu.service @@ -0,0 +1,14 @@ +# Oneshot midplane network service + +[Unit] +Description=Midplane network service +Requires=systemd-networkd.service +After=systemd-networkd.service + +[Service] +Type=oneshot +User=root +ExecStart=/usr/lib/systemd/systemd-networkd-wait-online -i bridge-midplane + +[Install] +WantedBy=multi-user.target diff --git a/src/systemd-sonic-generator/ssg-test.cc b/src/systemd-sonic-generator/ssg-test.cc index 2beabe2b33..e8f2449d85 100644 --- a/src/systemd-sonic-generator/ssg-test.cc +++ b/src/systemd-sonic-generator/ssg-test.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ namespace SSGTest { */ const std::string TEST_ROOT_DIR = "tests/ssg-test/"; const std::string TEST_UNIT_FILE_PREFIX = TEST_ROOT_DIR + "systemd/"; +const std::string TEST_LIB_NETWORK = TEST_UNIT_FILE_PREFIX + "network/"; 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"; @@ -50,23 +52,26 @@ 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_ETC_NETWORK = TEST_OUTPUT_DIR + "network/"; 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 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 */ - "midplane-network.service", /* A midplane network service for smart switch*/ - "database.service", /* A database service*/ - "database@.service", /* A database service for multi instances */ + "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 */ + "midplane-network-npu.service", /* A midplane network service for smart switch NPU*/ + "midplane-network-dpu.service", /* A midplane network service for smart switch DPU*/ + "database.service", /* A database service*/ + "database@.service", /* A database service for multi instances */ }; static std::mutex g_ssg_test_mutex; @@ -126,8 +131,13 @@ class SsgFunctionTest : public SystemdSonicGeneratorFixture { try { fs::path current(file->path()); if(!fs::is_directory(current)) { + std::string ext = boost::filesystem::extension(current); + fs::path dest_path = dest_dir; + if (ext == ".netdev" || ext == ".network" || ext == ".link") { + dest_path = dest_path / "network"; + } /* Copy file */ - fs::copy_file( current, dest_dir / current.filename()); + fs::copy_file( current, dest_path / current.filename()); } } catch(fs::filesystem_error const & e) { @@ -148,6 +158,10 @@ class SsgFunctionTest : public SystemdSonicGeneratorFixture { fs::create_directories(path); path = fs::path(TEST_PLATFORM_DIR.c_str()); fs::create_directories(path); + path = fs::path(TEST_LIB_NETWORK.c_str()); + fs::create_directories(path); + path = fs::path(TEST_ETC_NETWORK.c_str()); + fs::create_directories(path); fp = fopen(TEST_MACHINE_CONF.c_str(), "w"); ASSERT_NE(fp, nullptr); fputs("onie_platform=test_platform", fp); @@ -262,7 +276,10 @@ class SsgMainTest : public SsgFunctionTest { finished = true; } fs::path path{TEST_OUTPUT_DIR + target + "/" + str_t}; - EXPECT_EQ(fs::exists(path), expected_result) + char resolved_path[PATH_MAX]; + realpath(path.c_str(), resolved_path); + bool exist = fs::exists(path) && strcmp(resolved_path, "/dev/null") != 0; + EXPECT_EQ(exist, expected_result) << "Failed validation: " << path; } } @@ -293,7 +310,8 @@ class SsgMainTest : public SsgFunctionTest { */ validate_output_dependency_list(single_asic_dependency_list_split, test_service, IS_SINGLE_ASIC(cfg.num_asics), cfg.num_asics); - + validate_output_dependency_list(npu_dependency_list, + "midplane-network-npu.service", true, cfg.num_dpus); } else { /* Validate Unit file dependency creation for single instance * services. These entries should not be present for multi asic @@ -309,15 +327,6 @@ class SsgMainTest : public SsgFunctionTest { */ validate_output_dependency_list(common_dependency_list, 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); } /* @@ -331,8 +340,14 @@ class SsgMainTest : public SsgFunctionTest { test_target, IS_SINGLE_ASIC(cfg.num_asics), cfg.num_asics); validate_output_unit_files(common_service_list, 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); + validate_output_unit_files(npu_service_list, + test_target, cfg.is_smart_switch_npu, cfg.num_dpus); + validate_output_unit_files(npu_network_service_list, + "network", cfg.is_smart_switch_npu, cfg.num_dpus); + validate_output_unit_files(dpu_service_list, + test_target, cfg.is_smart_switch_dpu, cfg.num_dpus); + validate_output_unit_files(dpu_network_service_list, + "network", cfg.is_smart_switch_dpu, cfg.num_dpus); } /* ssg_main test routine. @@ -353,6 +368,11 @@ class SsgMainTest : public SsgFunctionTest { g_machine_config_file = TEST_MACHINE_CONF.c_str(); g_asic_conf_format = TEST_ASIC_CONF_FORMAT.c_str(); g_platform_file_format = TEST_PLATFORM_CONF_FORMAT.c_str(); + std::string lib_systemd = fs::current_path().string() + "/" + TEST_UNIT_FILE_PREFIX; + g_lib_systemd = lib_systemd.c_str(); + std::string etc_systemd = fs::current_path().string() + "/" + TEST_OUTPUT_DIR; + g_etc_systemd = etc_systemd.c_str(); + /* Set NUM_ASIC value in asic.conf */ fp = fopen(TEST_ASIC_CONF.c_str(), "w"); @@ -402,6 +422,13 @@ class SsgMainTest : public SsgFunctionTest { /* Save global variables before running tests */ virtual void SetUp() { SsgFunctionTest::SetUp(); + // Create /dev/null symlink for simulation disabled service + std::vector disabled_service; + disabled_service.insert(disabled_service.end(), npu_network_service_list.begin(), npu_network_service_list.end()); + disabled_service.insert(disabled_service.end(), dpu_network_service_list.begin(), dpu_network_service_list.end()); + for (const auto &service : disabled_service) { + fs::create_symlink("/dev/null", TEST_ETC_NETWORK + service); + } } /* Restore global vars */ @@ -414,12 +441,15 @@ class SsgMainTest : public SsgFunctionTest { static const std::vector single_asic_service_list; static const std::vector multi_asic_service_list; static const std::vector common_service_list; - static const std::vector smart_switch_service_list; + static const std::vector npu_service_list; + static const std::vector npu_network_service_list; + static const std::vector dpu_service_list; + static const std::vector dpu_network_service_list; static const std::vector single_asic_dependency_list; static const std::vector single_asic_dependency_list_split; static const std::vector multi_asic_dependency_list; + static const std::vector npu_dependency_list; static const std::vector common_dependency_list; - static const std::vector smart_switch_dependency_list; }; /* @@ -454,11 +484,31 @@ SsgMainTest::common_service_list = { "database.service", }; -/* Common Systemd service Unit file list for single and multi asic system. */ +/* Systemd service Unit file list for Smart Switch NPU. */ const std::vector -SsgMainTest::smart_switch_service_list = { +SsgMainTest::npu_service_list = { "database@dpu%1%.service", - "midplane-network.service", + "midplane-network-npu.service", +}; + +/* Systemd service Unit file list for Smart Switch NPU. */ +const std::vector +SsgMainTest::npu_network_service_list = { + "bridge-midplane.netdev", + "bridge-midplane.network", + "midplane-network-npu.network", +}; + +/* Systemd service Unit file list for Smart Switch DPU. */ +const std::vector +SsgMainTest::dpu_service_list = { + "midplane-network-dpu.service", +}; + +/* Systemd service Unit file list for Smart Switch DPU. */ +const std::vector +SsgMainTest::dpu_network_service_list = { + "midplane-network-dpu.network", }; /* @@ -503,9 +553,8 @@ SsgMainTest::common_dependency_list = { }; const std::vector -SsgMainTest::smart_switch_dependency_list = { - "Requires=midplane-network.service", - "After=midplane-network.service", +SsgMainTest::npu_dependency_list = { + "Before=database@dpu%1%.service", }; /* Test get functions for global vasr*/ @@ -590,15 +639,22 @@ TEST_F(SsgFunctionTest, get_num_of_asic) { /* TEST get_unit_files()*/ TEST_F(SsgFunctionTest, get_unit_files) { g_unit_file_prefix = TEST_UNIT_FILE_PREFIX.c_str(); + g_lib_systemd = TEST_UNIT_FILE_PREFIX.c_str(); + g_etc_systemd = TEST_OUTPUT_DIR.c_str(); g_config_file = TEST_CONFIG_FILE.c_str(); char* unit_files[NUM_UNIT_FILES] = { NULL }; int num_unit_files = get_unit_files(unit_files); - // Exclude the midplane-network.service which is only used for smart switch + // Exclude the midplane-network-{npu/dpu}.service which is only used for smart switch 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"), + "midplane-network-npu.service"), + non_smart_switch_generated_services.end()); + non_smart_switch_generated_services.erase( + std::remove(non_smart_switch_generated_services.begin(), + non_smart_switch_generated_services.end(), + "midplane-network-dpu.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) { diff --git a/src/systemd-sonic-generator/systemd-sonic-generator.c b/src/systemd-sonic-generator/systemd-sonic-generator.c index 28fad07276..87cd7dc940 100644 --- a/src/systemd-sonic-generator/systemd-sonic-generator.c +++ b/src/systemd-sonic-generator/systemd-sonic-generator.c @@ -18,6 +18,8 @@ +const char* LIB_SYSTEMD = "/usr/lib/systemd"; +const char* ETC_SYSTEMD = "/etc/systemd"; 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"; @@ -25,6 +27,17 @@ 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_lib_systemd = NULL; +const char* get_lib_systemd () { + return (g_lib_systemd) ? g_lib_systemd : LIB_SYSTEMD; +} + +const char* g_etc_systemd = NULL; +const char* get_etc_systemd () { + return (g_etc_systemd) ? g_etc_systemd : ETC_SYSTEMD; +} + 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; @@ -115,6 +128,22 @@ void strip_trailing_newline(char* str) { } +/** + * Checks if the given path is "/dev/null". + * + * @param path The path to check. + * @return true if the path is "/dev/null", false otherwise. + */ +static bool is_devnull(const char* path) +{ + char resolved_path[PATH_MAX]; + if (realpath(path, resolved_path) == NULL) { + return false; + } + return strcmp(resolved_path, "/dev/null") == 0; +} + + static int get_target_lines(char* unit_file, char* target_lines[]) { /*** Gets installation information for a given unit file @@ -468,11 +497,11 @@ int get_unit_files(char* unit_files[]) { if ((strcmp(line, "topology.service") == 0) && (num_asics == 1)) { continue; - } - - /* midplane-network service to be started only for smart switch platform */ - if ((strcmp(line, "midplane-network.service") == 0) && - !smart_switch) { + } else if ((strcmp(line, "midplane-network-dpu.service") == 0) && + !smart_switch_dpu) { + continue; + } else if ((strcmp(line, "midplane-network-npu.service") == 0) && + !smart_switch_npu) { continue; } @@ -580,6 +609,13 @@ static int create_symlink(char* unit, char* target, char* install_dir, int insta } } + if (is_devnull(dest_path)) { + if (remove(dest_path) == -1) { + fprintf(stderr, "Unable to remove existing symlink %s\n", dest_path); + return -1; + } + } + r = symlink(src_path, dest_path); if (r < 0) { @@ -873,102 +909,196 @@ static int get_num_of_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); +/** + * Installs the network service. + * + * This function installs the network service by creating a symlink + * to the network service file in the appropriate directory. + * + * @param unit_name The name of the network unit to install. + * @return 0 if the network unit is installed successfully, or -1 if an error occurs. + */ +static int install_network_unit(const char* unit_name) { + assert(unit_name); - if (!smart_switch || num_unit_files <= 0) { - return 0; + const char* unit_type = strrchr(unit_name, '.'); + if (unit_type == NULL) { + fprintf(stderr, "Invalid network unit %s\n", unit_name); + return -1; } + unit_type++; - // 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"); + char install_path[PATH_MAX] = {0}; + char original_path[PATH_MAX] = {0}; + const char* subdir; + if (strcmp(unit_type, "netdev") == 0 || strcmp(unit_type, "network") == 0) { + subdir = "/network/"; + } else { + fprintf(stderr, "Invalid network unit %s\n", unit_type); 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"; + strcpy(install_path, get_etc_systemd()); + strcat(install_path, subdir); + strcat(install_path, unit_name); + strcpy(original_path, get_lib_systemd()); + strcat(original_path, subdir); + strcat(original_path, unit_name); + + struct stat st; + + if (stat((const char *)install_path, &st) == 0) { + // If the file already exists, remove it + if (S_ISDIR(st.st_mode)) { + fprintf(stderr, "Error: %s is a directory\n", install_path); + return -1; + } + if (remove(install_path) != 0) { + fprintf(stderr, "Error removing existing file %s\n", install_path); + return -1; + } } - 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); + if (is_devnull(install_path)) { + if (remove(install_path) == -1) { + fprintf(stderr, "Unable to remove existing symlink %s\n", install_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; + } + + if (symlink(original_path, install_path) != 0) { + if (errno == EEXIST) + return 0; + fprintf(stderr, "Error creating symlink %s -> %s (%s)\n", install_path, original_path, strerror(errno)); + return -1; + } + + return 0; +} + + +static int render_network_service_for_smart_switch() { + if (!smart_switch_npu) { + return 0; + } + + // Render Before instruction for midplane network with database service + if (num_dpus == 0) { + return 0; + } + + char buffer_instruction[MAX_BUF_SIZE] = {0}; + strcpy(buffer_instruction, "\nBefore="); + for (size_t i = 0; i < num_dpus; i++) { + char *unit; + asprintf(&unit, "database@dpu%ld.service", i); + strcat(buffer_instruction, unit); + free(unit); + if (i != num_dpus - 1) { + strcat(buffer_instruction, " "); } + } + + char unit_path[PATH_MAX] = { 0 }; + strcpy(unit_path, get_unit_file_prefix()); + strcat(unit_path, "/midplane-network-npu.service"); + + 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); + size_t file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + size_t len = file_size + strlen(buffer_instruction) + 1; + char *unit_content = malloc(len); + if (unit_content == NULL) { + fprintf(stderr, "Failed to allocate memory for %s\n", unit_path); + } + if (fread(unit_content, file_size, 1, fp) != 1) { + fprintf(stderr, "Failed to read %s\n", unit_path); + free(unit_content); 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; + return -1; + } + fclose(fp); + + // Find insert point for Before instruction + char *insert_point = strstr(unit_content, "[Unit]"); + insert_point += strlen("[Unit]"); + // Move the rest of the file to make room for the Before instruction + memmove(insert_point + strlen(buffer_instruction), insert_point, file_size - (insert_point - unit_content)); + // Insert the Before instruction + memcpy(insert_point, buffer_instruction, strlen(buffer_instruction)); + // Remove original Before instruction + insert_point += strlen(buffer_instruction); + char *before_start = strstr(insert_point, "Before="); + while (before_start != NULL) { + char *before_end = strchr(before_start, '\n'); + if (before_end == NULL) { + before_end = before_start + strlen(before_start); + } else { + // Include newline character + before_end += 1; } - // 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; + const char *target_service = strstr(before_start, "database@dpu"); + if (target_service != NULL && target_service < before_end) { + memmove(before_start, before_end, strlen(before_end) + 1); + } else { + before_start = before_end; } + before_start = strstr(before_start, "Before="); + } + // Write the modified unit file + fp = fopen(unit_path, "w"); + if (fp == NULL) { + fprintf(stderr, "Failed to open %s\n", unit_path); + free(unit_content); + return -1; + } + if (fwrite(unit_content, strlen(unit_content), 1, fp) != 1) { + fprintf(stderr, "Failed to write %s\n", unit_path); + free(unit_content); fclose(fp); - free(data); - unit++; + return -1; + } + fclose(fp); + free(unit_content); + + return 0; +} + + +static int install_network_service_for_smart_switch() { + const char** network_units = NULL; + if (smart_switch_npu) { + static const char* npu_network_units[] = { + "bridge-midplane.netdev", + "bridge-midplane.network", + "midplane-network-npu.network", + NULL + }; + network_units = npu_network_units; + } else if (smart_switch_dpu) { + static const char* dpu_network_units[] = { + "midplane-network-dpu.network", + NULL + }; + network_units = dpu_network_units; + } else { + return -1; + } + + if (network_units == NULL) { + return 0; + } + + while(*network_units) { + if (install_network_unit(*network_units) != 0) { + return -1; + } + network_units++; } return 0; @@ -1006,10 +1136,13 @@ int ssg_main(int argc, char **argv) { strcat(install_dir, "/"); num_unit_files = get_unit_files(unit_files); - // Revise dependency chain for smart switch + // Install and render midplane network service for smart switch if (smart_switch) { - if (revise_smart_switch_dependency_chain(num_unit_files, (const char **)unit_files) != 0) { - return 1; + if (render_network_service_for_smart_switch() != 0) { + return -1; + } + if (install_network_service_for_smart_switch() != 0) { + return -1; } } diff --git a/src/systemd-sonic-generator/systemd-sonic-generator.h b/src/systemd-sonic-generator/systemd-sonic-generator.h index fd942c2bd3..8695951fb2 100644 --- a/src/systemd-sonic-generator/systemd-sonic-generator.h +++ b/src/systemd-sonic-generator/systemd-sonic-generator.h @@ -16,7 +16,9 @@ extern const char* CONFIG_FILE; extern const char* MACHINE_CONF_FILE; extern const char* ASIC_CONF_FORMAT; extern const char* PLATFORM_FILE_FORMAT; -extern const char* g_unit_file_prefix; +extern const char* g_lib_systemd; +extern const char* g_etc_systemd; +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; diff --git a/src/systemd-sonic-generator/tests/testfiles/bridge-midplane.netdev b/src/systemd-sonic-generator/tests/testfiles/bridge-midplane.netdev new file mode 120000 index 0000000000..a866bb529e --- /dev/null +++ b/src/systemd-sonic-generator/tests/testfiles/bridge-midplane.netdev @@ -0,0 +1 @@ +../../../../files/image_config/midplane-network/bridge-midplane.netdev \ No newline at end of file diff --git a/src/systemd-sonic-generator/tests/testfiles/bridge-midplane.network b/src/systemd-sonic-generator/tests/testfiles/bridge-midplane.network new file mode 120000 index 0000000000..cb7ad49651 --- /dev/null +++ b/src/systemd-sonic-generator/tests/testfiles/bridge-midplane.network @@ -0,0 +1 @@ +../../../../files/image_config/midplane-network/bridge-midplane.network \ No newline at end of file diff --git a/src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.network b/src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.network new file mode 120000 index 0000000000..e042f502c7 --- /dev/null +++ b/src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.network @@ -0,0 +1 @@ +../../../../files/image_config/midplane-network/midplane-network-dpu.network \ No newline at end of file diff --git a/src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.service b/src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.service new file mode 120000 index 0000000000..e11f6138fb --- /dev/null +++ b/src/systemd-sonic-generator/tests/testfiles/midplane-network-dpu.service @@ -0,0 +1 @@ +../../../../files/image_config/midplane-network/midplane-network-dpu.service \ No newline at end of file diff --git a/src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.network b/src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.network new file mode 120000 index 0000000000..6692158915 --- /dev/null +++ b/src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.network @@ -0,0 +1 @@ +../../../../files/image_config/midplane-network/midplane-network-npu.network \ No newline at end of file diff --git a/src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.service b/src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.service new file mode 120000 index 0000000000..0a8b423a46 --- /dev/null +++ b/src/systemd-sonic-generator/tests/testfiles/midplane-network-npu.service @@ -0,0 +1 @@ +../../../../files/image_config/midplane-network/midplane-network-npu.service \ No newline at end of file diff --git a/src/systemd-sonic-generator/tests/testfiles/midplane-network.service b/src/systemd-sonic-generator/tests/testfiles/midplane-network.service deleted file mode 100644 index 757e38f343..0000000000 --- a/src/systemd-sonic-generator/tests/testfiles/midplane-network.service +++ /dev/null @@ -1,12 +0,0 @@ -[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