@@ -339,6 +339,44 @@ def test_port_init_done_twice(self):
339339 feature_handler .port_listener (key = 'PortInitDone' , op = 'SET' , data = None )
340340 feature_handler .enable_delayed_services .assert_called_once ()
341341
342+ @mock .patch ("syslog.syslog" , side_effect = syslog_side_effect )
343+ def test_enable_and_disable_feature_whitelist_skips_actions (mock_syslog ):
344+ """Verify that whitelisted feature 'frr_bmp' is skipped in both enable and disable."""
345+ feature_state_table_mock = mock .Mock ()
346+ device_cfg = {"DEVICE_METADATA" : {"localhost" : {"type" : "FixedSwitch" }}}
347+ handler = featured .FeatureHandler (MockConfigDb (), feature_state_table_mock , device_cfg , False )
348+
349+ # Shared feature config (frr_bmp is whitelisted)
350+ feat_cfg = {"state" : "enabled" , "auto_restart" : "enabled" }
351+ feature = featured .Feature ("frr_bmp" , feat_cfg , device_cfg )
352+
353+ with mock .patch .object (handler , "get_multiasic_feature_instances" ,
354+ return_value = (["frr_bmp" ], ["service" ])), \
355+ mock .patch .object (handler , "get_systemd_unit_state" , return_value = "disabled" ), \
356+ mock .patch ("featured.run_cmd" ) as mocked_run_cmd , \
357+ mock .patch .object (handler , "set_feature_state" ) as mocked_set_state :
358+
359+ # --- enable_feature() ---
360+ handler .enable_feature (feature )
361+
362+ mocked_run_cmd .assert_not_called ()
363+ mocked_set_state .assert_not_called ()
364+ assert any ("Whitelist: skip enabling 'frr_bmp'" in str (c .args [1 ]) for c in mock_syslog .call_args_list )
365+
366+ mock_syslog .reset_mock ()
367+ with mock .patch .object (handler , "get_multiasic_feature_instances" ,
368+ return_value = (["frr_bmp" ], ["service" ])), \
369+ mock .patch .object (handler , "get_systemd_unit_state" , return_value = "enabled" ), \
370+ mock .patch ("featured.run_cmd" ) as mocked_run_cmd , \
371+ mock .patch .object (handler , "set_feature_state" ) as mocked_set_state :
372+
373+ # --- disable_feature() ---
374+ handler .disable_feature (feature )
375+
376+ mocked_run_cmd .assert_not_called ()
377+ mocked_set_state .assert_not_called ()
378+ assert any ("Whitelist: skip disabling 'frr_bmp'" in str (c .args [1 ]) for c in mock_syslog .call_args_list )
379+
342380
343381@mock .patch ("syslog.syslog" , side_effect = syslog_side_effect )
344382@mock .patch ('sonic_py_common.device_info.get_device_runtime_metadata' )
@@ -361,8 +399,7 @@ def tearDown(self):
361399
362400 def test_feature_events (self , mock_syslog , get_runtime ):
363401 MockSelect .set_event_queue ([('FEATURE' , 'dhcp_relay' ),
364- ('FEATURE' , 'mux' ),
365- ('FEATURE' , 'telemetry' )])
402+ ('FEATURE' , 'mux' )])
366403 with mock .patch ('featured.subprocess' ) as mocked_subprocess :
367404 popen_mock = mock .Mock ()
368405 attrs = {'communicate.return_value' : ('output' , 'error' )}
@@ -401,7 +438,6 @@ def test_feature_events(self, mock_syslog, get_runtime):
401438 def test_delayed_service (self , mock_syslog , get_runtime ):
402439 MockSelect .set_event_queue ([('FEATURE' , 'dhcp_relay' ),
403440 ('FEATURE' , 'mux' ),
404- ('FEATURE' , 'telemetry' ),
405441 ('PORT_TABLE' , 'PortInitDone' )])
406442 # Note: To simplify testing, subscriberstatetable only read from CONFIG_DB
407443 MockConfigDb .CONFIG_DB ['PORT_TABLE' ] = {'PortInitDone' : {'lanes' : '0' }, 'PortConfigDone' : {'val' : 'true' }}
@@ -424,11 +460,7 @@ def test_delayed_service(self, mock_syslog, get_runtime):
424460 call (['sudo' , 'systemctl' , 'daemon-reload' ], capture_output = True , check = True , text = True ),
425461 call (['sudo' , 'systemctl' , 'unmask' , 'mux.service' ], capture_output = True , check = True , text = True ),
426462 call (['sudo' , 'systemctl' , 'enable' , 'mux.service' ], capture_output = True , check = True , text = True ),
427- call (['sudo' , 'systemctl' , 'start' , 'mux.service' ], capture_output = True , check = True , text = True ),
428- call (['sudo' , 'systemctl' , 'daemon-reload' ], capture_output = True , check = True , text = True ),
429- call (['sudo' , 'systemctl' , 'unmask' , 'telemetry.service' ], capture_output = True , check = True , text = True ),
430- call (['sudo' , 'systemctl' , 'enable' , 'telemetry.service' ], capture_output = True , check = True , text = True ),
431- call (['sudo' , 'systemctl' , 'start' , 'telemetry.service' ], capture_output = True , check = True , text = True )]
463+ call (['sudo' , 'systemctl' , 'start' , 'mux.service' ], capture_output = True , check = True , text = True )]
432464
433465 mocked_subprocess .run .assert_has_calls (expected , any_order = True )
434466
@@ -454,20 +486,15 @@ def test_advanced_reboot(self, mock_syslog, get_runtime):
454486 call (['sudo' , 'systemctl' , 'daemon-reload' ], capture_output = True , check = True , text = True ),
455487 call (['sudo' , 'systemctl' , 'unmask' , 'mux.service' ], capture_output = True , check = True , text = True ),
456488 call (['sudo' , 'systemctl' , 'enable' , 'mux.service' ], capture_output = True , check = True , text = True ),
457- call (['sudo' , 'systemctl' , 'start' , 'mux.service' ], capture_output = True , check = True , text = True ),
458- call (['sudo' , 'systemctl' , 'daemon-reload' ], capture_output = True , check = True , text = True ),
459- call (['sudo' , 'systemctl' , 'unmask' , 'telemetry.service' ], capture_output = True , check = True , text = True ),
460- call (['sudo' , 'systemctl' , 'enable' , 'telemetry.service' ], capture_output = True , check = True , text = True ),
461- call (['sudo' , 'systemctl' , 'start' , 'telemetry.service' ], capture_output = True , check = True , text = True )]
489+ call (['sudo' , 'systemctl' , 'start' , 'mux.service' ], capture_output = True , check = True , text = True )]
462490
463491 mocked_subprocess .run .assert_has_calls (expected , any_order = True )
464492
465493 def test_portinit_timeout (self , mock_syslog , get_runtime ):
466494 print (MockConfigDb .CONFIG_DB )
467495 MockSelect .NUM_TIMEOUT_TRIES = 1
468496 MockSelect .set_event_queue ([('FEATURE' , 'dhcp_relay' ),
469- ('FEATURE' , 'mux' ),
470- ('FEATURE' , 'telemetry' )])
497+ ('FEATURE' , 'mux' )])
471498 with mock .patch ('featured.subprocess' ) as mocked_subprocess :
472499 popen_mock = mock .Mock ()
473500 attrs = {'communicate.return_value' : ('output' , 'error' )}
@@ -487,9 +514,5 @@ def test_portinit_timeout(self, mock_syslog, get_runtime):
487514 call (['sudo' , 'systemctl' , 'daemon-reload' ], capture_output = True , check = True , text = True ),
488515 call (['sudo' , 'systemctl' , 'unmask' , 'mux.service' ], capture_output = True , check = True , text = True ),
489516 call (['sudo' , 'systemctl' , 'enable' , 'mux.service' ], capture_output = True , check = True , text = True ),
490- call (['sudo' , 'systemctl' , 'start' , 'mux.service' ], capture_output = True , check = True , text = True ),
491- call (['sudo' , 'systemctl' , 'daemon-reload' ], capture_output = True , check = True , text = True ),
492- call (['sudo' , 'systemctl' , 'unmask' , 'telemetry.service' ], capture_output = True , check = True , text = True ),
493- call (['sudo' , 'systemctl' , 'enable' , 'telemetry.service' ], capture_output = True , check = True , text = True ),
494- call (['sudo' , 'systemctl' , 'start' , 'telemetry.service' ], capture_output = True , check = True , text = True )]
517+ call (['sudo' , 'systemctl' , 'start' , 'mux.service' ], capture_output = True , check = True , text = True )]
495518 mocked_subprocess .run .assert_has_calls (expected , any_order = True )
0 commit comments