Skip to content

Commit 33c7d61

Browse files
authored
Revert "Revert "[featured] fix non existing feature start (#234)" (#273)"
This reverts commit 0206da1.
1 parent 694a2c6 commit 33c7d61

2 files changed

Lines changed: 60 additions & 6 deletions

File tree

scripts/featured

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,13 @@ class FeatureHandler(object):
266266
return True
267267

268268
if enable:
269-
self.enable_feature(feature)
269+
if not self.enable_feature(feature):
270+
return False
270271
syslog.syslog(syslog.LOG_INFO, "Feature {} is enabled and started".format(feature.name))
271272

272273
if disable:
273-
self.disable_feature(feature)
274+
if not self.disable_feature(feature):
275+
return False
274276
syslog.syslog(syslog.LOG_INFO, "Feature {} is stopped and disabled".format(feature.name))
275277

276278
return True
@@ -405,7 +407,7 @@ class FeatureHandler(object):
405407
for feature_name in feature_names:
406408
# Check if it is already enabled, if yes skip the system call
407409
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
408-
if unit_file_state == "enabled" or not unit_file_state:
410+
if unit_file_state == "enabled":
409411
continue
410412
cmds = []
411413
for suffix in feature_suffixes:
@@ -425,16 +427,17 @@ class FeatureHandler(object):
425427
syslog.syslog(syslog.LOG_ERR, "Feature '{}.{}' failed to be enabled and started"
426428
.format(feature.name, feature_suffixes[-1]))
427429
self.set_feature_state(feature, self.FEATURE_STATE_FAILED)
428-
return
430+
return False
429431

430432
self.set_feature_state(feature, self.FEATURE_STATE_ENABLED)
433+
return True
431434

432435
def disable_feature(self, feature):
433436
feature_names, feature_suffixes = self.get_multiasic_feature_instances(feature)
434437
for feature_name in feature_names:
435438
# Check if it is already disabled, if yes skip the system call
436439
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
437-
if unit_file_state in ("disabled", "masked") or not unit_file_state:
440+
if unit_file_state in ("disabled", "masked"):
438441
continue
439442
cmds = []
440443
for suffix in reversed(feature_suffixes):
@@ -449,9 +452,10 @@ class FeatureHandler(object):
449452
syslog.syslog(syslog.LOG_ERR, "Feature '{}.{}' failed to be stopped and disabled"
450453
.format(feature.name, feature_suffixes[-1]))
451454
self.set_feature_state(feature, self.FEATURE_STATE_FAILED)
452-
return
455+
return False
453456

454457
self.set_feature_state(feature, self.FEATURE_STATE_DISABLED)
458+
return True
455459

456460
def resync_feature_state(self, feature):
457461
current_entry = self._config_db.get_entry('FEATURE', feature.name)

tests/featured/featured_test.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,3 +493,53 @@ def test_portinit_timeout(self, mock_syslog, get_runtime):
493493
call(['sudo', 'systemctl', 'enable', 'telemetry.service'], capture_output=True, check=True, text=True),
494494
call(['sudo', 'systemctl', 'start', 'telemetry.service'], capture_output=True, check=True, text=True)]
495495
mocked_subprocess.run.assert_has_calls(expected, any_order=True)
496+
497+
def test_systemctl_command_failure(self, mock_syslog, get_runtime):
498+
"""Test that when systemctl commands fail:
499+
1. The feature state is not cached
500+
2. The feature state is set to FAILED
501+
3. The update_feature_state returns False
502+
"""
503+
mock_db = mock.MagicMock()
504+
mock_feature_state_table = mock.MagicMock()
505+
506+
feature_handler = featured.FeatureHandler(mock_db, mock_feature_state_table, {}, False)
507+
feature_handler.is_delayed_enabled = True
508+
509+
# Create a feature that should be enabled
510+
feature_name = 'test_feature'
511+
feature_cfg = {
512+
'state': 'enabled',
513+
'auto_restart': 'enabled',
514+
'delayed': 'False',
515+
'has_global_scope': 'True',
516+
'has_per_asic_scope': 'False'
517+
}
518+
519+
# Initialize the feature in cached_config using the same pattern as in featured
520+
feature = featured.Feature(feature_name, feature_cfg)
521+
feature_handler._cached_config.setdefault(feature_name, featured.Feature(feature_name, {}))
522+
523+
# Mock subprocess.run and Popen to simulate command failure
524+
with mock.patch('featured.subprocess') as mocked_subprocess:
525+
# Mock Popen for get_systemd_unit_state
526+
popen_mock = mock.Mock()
527+
popen_mock.communicate.return_value = ('enabled', '')
528+
popen_mock.returncode = 1
529+
mocked_subprocess.Popen.return_value = popen_mock
530+
531+
# Mock run_cmd to raise an exception
532+
with mock.patch('featured.run_cmd') as mocked_run_cmd:
533+
mocked_run_cmd.side_effect = Exception("Command failed")
534+
535+
# Try to update feature state
536+
result = feature_handler.update_feature_state(feature)
537+
538+
# Verify the result is False
539+
assert result is False
540+
541+
# Verify the feature state was set to FAILED
542+
mock_feature_state_table.set.assert_called_with('test_feature', [('state', 'failed')])
543+
544+
# Verify the feature state was not enabled in the cache
545+
assert feature_handler._cached_config[feature.name].state != 'enabled'

0 commit comments

Comments
 (0)