Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Fix for bug-21459: Set the initial DPU admin state #588

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion sonic-chassisd/scripts/chassisd
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ class SmartSwitchModuleUpdater(ModuleUpdater):
fvs = dict(fvs[-1])
return fvs[CHASSIS_MODULE_ADMIN_STATUS]
else:
return 'down'
return 'empty'

def module_db_update(self):
for module_index in range(0, self.num_modules):
Expand Down Expand Up @@ -1261,6 +1261,48 @@ class ChassisdDaemon(daemon_base.DaemonBase):
else:
self.log_warning("Caught unhandled signal '{}' - ignoring...".format(SIGNALS_TO_NAMES_DICT[sig]))

def submit_dpu_callback(self, module_index, admin_state):
try_get(self.module_updater.chassis.get_module(module_index).set_admin_state, admin_state, default=False)
pass

def set_initial_dpu_admin_state(self):
"""Send admin_state trigger once to modules those are powered up"""
threads = []
for module_index in range(0, self.module_updater.num_modules):
op = None
# Get operational state of DPU
module_name = self.platform_chassis.get_module(module_index).get_name()
operational_state = self.platform_chassis.get_module(module_index).get_oper_status()

try:
# Get admin state of DPU
admin_state = self.module_updater.get_module_admin_status(module_name)
if admin_state == 'empty' and operational_state != ModuleBase.MODULE_STATUS_OFFLINE:
# shutdown DPU
op = MODULE_ADMIN_DOWN

# Initialize DPU_STATE DB table on bootup
dpu_state_key = "DPU_STATE|" + module_name
if operational_state == ModuleBase.MODULE_STATUS_ONLINE:
op_state = 'up'
else:
op_state = 'down'
self.module_updater.update_dpu_state(dpu_state_key, op_state)

if op is not None:
# Create and start a thread for the DPU logic
thread = threading.Thread(target=self.submit_dpu_callback, args=(module_index, op))
thread.daemon = True # Set as a daemon thread
thread.start()
threads.append(thread)

except Exception as e:
self.log_error(f"Error in run: {str(e)}", exc_info=True)

# Wait for all threads to finish
for thread in threads:
thread.join()

# Run daemon
def run(self):
self.log_info("Starting up...")
Expand Down Expand Up @@ -1296,6 +1338,10 @@ class ChassisdDaemon(daemon_base.DaemonBase):
# Start main loop
self.log_info("Start daemon main loop")

# Set the initial DPU admin state for SmartSwitch
if self.smartswitch:
self.set_initial_dpu_admin_state()

while not self.stop.wait(CHASSIS_INFO_UPDATE_PERIOD_SECS):
self.module_updater.module_db_update()
self.module_updater.check_midplane_reachability()
Expand Down
179 changes: 179 additions & 0 deletions sonic-chassisd/tests/test_chassisd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,185 @@ def test_daemon_run_smartswitch():
with patch.object(module_updater, 'num_modules', 1):
daemon_chassisd.run()

def test_set_initial_dpu_admin_state_down():
# Test the chassisd run
chassis = MockSmartSwitchChassis()

# DPU0 details
index = 0
name = "DPU0"
desc = "DPU Module 0"
slot = 0
sup_slot = 0
serial = "DPU0-0000"
module_type = ModuleBase.MODULE_TYPE_DPU
module = MockModule(index, name, desc, module_type, slot, serial)
module.set_midplane_ip()

# Set initial state for DPU0
status = ModuleBase.MODULE_STATUS_PRESENT
module.set_oper_status(status)
chassis.module_list.append(module)

# Supervisor ModuleUpdater
module_updater = SmartSwitchModuleUpdater(SYSLOG_IDENTIFIER, chassis)
module_updater.module_db_update()
module_updater.modules_num_update()

# ChassisdDaemon setup
daemon_chassisd = ChassisdDaemon(SYSLOG_IDENTIFIER, chassis)
daemon_chassisd.module_updater = module_updater
daemon_chassisd.stop = MagicMock()
daemon_chassisd.stop.wait.return_value = True
daemon_chassisd.smartswitch = True

# Import platform and use chassis as platform_chassis
import sonic_platform.platform
platform_chassis = chassis

# Mock objects
mock_chassis = MagicMock()
mock_module_updater = MagicMock()

# Mock the module (DPU0)
mock_module = MagicMock()
mock_module.get_name.return_value = "DPU0"

# Mock chassis.get_module to return the mock_module for DPU0
def mock_get_module(index):
if index == 0: # For DPU0
return mock_module
return None # No other modules available in this test case

# Apply the side effect for chassis.get_module
mock_chassis.get_module.side_effect = mock_get_module

# Mock state_db
mock_state_db = MagicMock()
# fvs_mock = [True, {CHASSIS_MIDPLANE_INFO_ACCESS_FIELD: 'True'}]
# mock_state_db.get.return_value = fvs_mock

# Mock db_connect
mock_db_connect = MagicMock()
mock_db_connect.return_value = mock_state_db

# Mock admin_status
# mock_module_updater.get_module_admin_status.return_value = 'down'

# Set access of DPU0 Down
midplane_table = module_updater.midplane_table
module.set_midplane_reachable(True)
module_updater.check_midplane_reachability()
fvs = midplane_table.get(name)
assert fvs != None
if isinstance(fvs, list):
fvs = dict(fvs[-1])
assert module.get_midplane_ip() == fvs[CHASSIS_MIDPLANE_INFO_IP_FIELD]
assert str(module.is_midplane_reachable()) == fvs[CHASSIS_MIDPLANE_INFO_ACCESS_FIELD]

# Patching platform's Chassis object to return the mocked module
with patch.object(sonic_platform.platform.Chassis, 'is_smartswitch') as mock_is_smartswitch, \
patch.object(sonic_platform.platform.Chassis, 'get_module', side_effect=mock_get_module):

# Simulate that the system is a SmartSwitch
mock_is_smartswitch.return_value = True

# Patch num_modules for the updater
with patch.object(daemon_chassisd.module_updater, 'num_modules', 1), \
patch.object(daemon_chassisd.module_updater, 'get_module_admin_status', return_value='down'):
# Now run the function that sets the initial admin state
daemon_chassisd.set_initial_dpu_admin_state()


def test_set_initial_dpu_admin_state_up():
# Test the chassisd run
chassis = MockSmartSwitchChassis()

# DPU0 details
index = 0
name = "DPU0"
desc = "DPU Module 0"
slot = 0
sup_slot = 0
serial = "DPU0-0000"
module_type = ModuleBase.MODULE_TYPE_DPU
module = MockModule(index, name, desc, module_type, slot, serial)
module.set_midplane_ip()

# Set initial state for DPU0
status = ModuleBase.MODULE_STATUS_PRESENT
module.set_oper_status(status)
chassis.module_list.append(module)

# Supervisor ModuleUpdater
module_updater = SmartSwitchModuleUpdater(SYSLOG_IDENTIFIER, chassis)
module_updater.module_db_update()
module_updater.modules_num_update()

# ChassisdDaemon setup
daemon_chassisd = ChassisdDaemon(SYSLOG_IDENTIFIER, chassis)
daemon_chassisd.module_updater = module_updater
daemon_chassisd.stop = MagicMock()
daemon_chassisd.stop.wait.return_value = True
daemon_chassisd.smartswitch = True

# Import platform and use chassis as platform_chassis
import sonic_platform.platform
platform_chassis = chassis

# Mock objects
mock_chassis = MagicMock()
mock_module_updater = MagicMock()

# Mock the module (DPU0)
mock_module = MagicMock()
mock_module.get_name.return_value = "DPU0"

# Mock chassis.get_module to return the mock_module for DPU0
def mock_get_module(index):
if index == 0: # For DPU0
return mock_module
return None # No other modules available in this test case

# Apply the side effect for chassis.get_module
mock_chassis.get_module.side_effect = mock_get_module

# Mock state_db
mock_state_db = MagicMock()
# fvs_mock = [True, {CHASSIS_MIDPLANE_INFO_ACCESS_FIELD: 'True'}]
# mock_state_db.get.return_value = fvs_mock

# Mock db_connect
mock_db_connect = MagicMock()
mock_db_connect.return_value = mock_state_db

# Mock admin_status
# mock_module_updater.get_module_admin_status.return_value = 'up'

# Set access of DPU0 up
midplane_table = module_updater.midplane_table
module.set_midplane_reachable(False)
module_updater.check_midplane_reachability()
fvs = midplane_table.get(name)
assert fvs != None
if isinstance(fvs, list):
fvs = dict(fvs[-1])
assert module.get_midplane_ip() == fvs[CHASSIS_MIDPLANE_INFO_IP_FIELD]
assert str(module.is_midplane_reachable()) == fvs[CHASSIS_MIDPLANE_INFO_ACCESS_FIELD]

# Patching platform's Chassis object to return the mocked module
with patch.object(sonic_platform.platform.Chassis, 'is_smartswitch') as mock_is_smartswitch, \
patch.object(sonic_platform.platform.Chassis, 'get_module', side_effect=mock_get_module):

# Simulate that the system is a SmartSwitch
mock_is_smartswitch.return_value = True

# Patch num_modules for the updater
with patch.object(daemon_chassisd.module_updater, 'num_modules', 1), \
patch.object(daemon_chassisd.module_updater, 'get_module_admin_status', return_value='up'):
# Now run the function that sets the initial admin state
daemon_chassisd.set_initial_dpu_admin_state()


def test_daemon_run_supervisor_invalid_slot():
chassis = MockChassis()
Expand Down
Loading