Skip to content

Commit 29be8d2

Browse files
committed
Added Support to render Feature Table using Device running metadata.
Also added support to render 'has_asic_scope' field of Feature Table. Signed-off-by: Abhishek Dosi <[email protected]>
1 parent f6ea036 commit 29be8d2

File tree

1 file changed

+64
-20
lines changed

1 file changed

+64
-20
lines changed

scripts/hostcfgd

+64-20
Original file line numberDiff line numberDiff line change
@@ -146,30 +146,31 @@ class Feature(object):
146146
"""
147147

148148
self.name = feature_name
149-
self.state = self._get_target_state(feature_cfg.get('state'), device_config or {})
149+
self.state = self._get_feature_table_key_render_value(feature_cfg.get('state'), device_config or {}, ['enabled', 'disabled', 'always_enabled', 'always_disabled'])
150150
self.auto_restart = feature_cfg.get('auto_restart', 'disabled')
151151
self.has_timer = safe_eval(feature_cfg.get('has_timer', 'False'))
152152
self.has_global_scope = safe_eval(feature_cfg.get('has_global_scope', 'True'))
153-
self.has_per_asic_scope = safe_eval(feature_cfg.get('has_per_asic_scope', 'False'))
153+
self.has_per_asic_scope = safe_eval(self._get_feature_table_key_render_value(feature_cfg.get('has_per_asic_scope', 'False'), device_config or {}, ['True', 'False']))
154154

155-
def _get_target_state(self, state_configuration, device_config):
156-
""" Returns the target state for the feature by rendering the state field as J2 template.
155+
def _get_feature_table_key_render_value(self, configuration, device_config, expected_values):
156+
""" Returns the target value for the feature by rendering the configuration as J2 template.
157157
158158
Args:
159-
state_configuration (str): State configuration from CONFIG_DB
160-
deviec_config (dict): DEVICE_METADATA section of CONFIG_DB
159+
configuration (str): Feature Table value from CONFIG_DB for given key
160+
device_config (dict): DEVICE_METADATA section of CONFIG_DB and populated Device Running Metadata
161+
expected_values (list): Expected set of Feature Table value for given key
161162
Returns:
162-
(str): Target feature state
163+
(str): Target feature table value for given key
163164
"""
164165

165-
if state_configuration is None:
166+
if configuration is None:
166167
return None
167168

168-
template = jinja2.Template(state_configuration)
169-
target_state = template.render(device_config)
170-
if target_state not in ('enabled', 'disabled', 'always_enabled', 'always_disabled'):
171-
raise ValueError('Invalid state rendered for feature {}: {}'.format(self.name, target_state))
172-
return target_state
169+
template = jinja2.Template(configuration)
170+
target_value = template.render(device_config)
171+
if target_value not in expected_values:
172+
raise ValueError('Invalid value rendered for feature {}: {}'.format(self.name, target_value))
173+
return target_value
173174

174175
def compare_state(self, feature_name, feature_cfg):
175176
if self.name != feature_name or not isinstance(feature_cfg, dict):
@@ -197,6 +198,7 @@ class FeatureHandler(object):
197198
self._device_config = device_config
198199
self._cached_config = {}
199200
self.is_multi_npu = device_info.is_multi_npu()
201+
self._device_running_config = device_info.get_device_runtime_metadata()
200202

201203
def handler(self, feature_name, op, feature_cfg):
202204
if not feature_cfg:
@@ -205,7 +207,7 @@ class FeatureHandler(object):
205207
self._feature_state_table._del(feature_name)
206208
return
207209

208-
feature = Feature(feature_name, feature_cfg, self._device_config)
210+
feature = Feature(feature_name, feature_cfg, self._device_config | self._device_running_config)
209211
self._cached_config.setdefault(feature_name, Feature(feature_name, {}))
210212

211213
# Change auto-restart configuration first.
@@ -230,14 +232,14 @@ class FeatureHandler(object):
230232
"""
231233
Summary:
232234
Updates the state field in the FEATURE|* tables as the state field
233-
might have to be rendered based on DEVICE_METADATA table
235+
might have to be rendered based on DEVICE_METADATA table and generated Device Running Metadata
234236
"""
235237
for feature_name in feature_table.keys():
236238
if not feature_name:
237239
syslog.syslog(syslog.LOG_WARNING, "Feature is None")
238240
continue
239241

240-
feature = Feature(feature_name, feature_table[feature_name], self._device_config)
242+
feature = Feature(feature_name, feature_table[feature_name], self._device_config | self._device_running_config)
241243

242244
self._cached_config.setdefault(feature_name, feature)
243245
self.update_systemd_config(feature)
@@ -283,8 +285,50 @@ class FeatureHandler(object):
283285
self.disable_feature(feature)
284286
syslog.syslog(syslog.LOG_INFO, "Feature {} is stopped and disabled".format(feature.name))
285287

288+
if self.is_multi_npu:
289+
self.sync_feature_asic_scope(feature)
290+
286291
return True
287292

293+
def sync_feature_asic_scope(self, feature_config):
294+
"""Updates the has_per_asic_scope field in the FEATURE|* tables as the field
295+
might have to be rendered based on DEVICE_METADATA table or Device Running configuration.
296+
Disable the ASIC instance service unit file it the render value is False and update config
297+
298+
Args:
299+
feature: An object represents a feature's configuration in `FEATURE`
300+
table of `CONFIG_DB`.
301+
302+
Returns:
303+
None.
304+
"""
305+
306+
cmds = []
307+
feature_names, feature_suffixes = self.get_multiasic_feature_instances(feature_config, True)
308+
for feature_name in feature_names:
309+
if '@' not in feature_name:
310+
continue
311+
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
312+
if not unit_file_state:
313+
continue
314+
if unit_file_state == "enabled" and not feature_config.has_per_asic_scope:
315+
for suffix in reversed(feature_suffixes):
316+
cmds.append("sudo systemctl stop {}.{}".format(feature_name, suffix))
317+
cmds.append("sudo systemctl disable {}.{}".format(feature_name, feature_suffixes[-1]))
318+
cmds.append("sudo systemctl mask {}.{}".format(feature_name, feature_suffixes[-1]))
319+
for cmd in cmds:
320+
syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd))
321+
try:
322+
run_cmd(cmd, raise_exception=True)
323+
except Exception as err:
324+
syslog.syslog(syslog.LOG_ERR, "Feature '{}.{}' failed to be stopped and disabled"
325+
.format(feature.name, feature_suffixes[-1]))
326+
self.set_feature_state(feature, self.FEATURE_STATE_FAILED)
327+
return
328+
self._config_db.mod_entry('FEATURE', feature_config.name, {'has_per_asic_scope': feature_config.has_per_asic_scope})
329+
330+
331+
288332
def update_systemd_config(self, feature_config):
289333
"""Updates `Restart=` field in feature's systemd configuration file
290334
according to the value of `auto_restart` field in `FEATURE` table of `CONFIG_DB`.
@@ -323,12 +367,12 @@ class FeatureHandler(object):
323367
except Exception as err:
324368
syslog.syslog(syslog.LOG_ERR, "Failed to reload systemd configuration files!")
325369

326-
def get_multiasic_feature_instances(self, feature):
370+
def get_multiasic_feature_instances(self, feature, all_instance=False):
327371
# Create feature name suffix depending feature is running in host or namespace or in both
328372
feature_names = (
329373
([feature.name] if feature.has_global_scope or not self.is_multi_npu else []) +
330374
([(feature.name + '@' + str(asic_inst)) for asic_inst in range(device_info.get_num_npus())
331-
if feature.has_per_asic_scope and self.is_multi_npu])
375+
if self.is_multi_npu and (all_instance or feature.has_per_asic_scope)])
332376
)
333377

334378
if not feature_names:
@@ -358,7 +402,7 @@ class FeatureHandler(object):
358402
for feature_name in feature_names:
359403
# Check if it is already enabled, if yes skip the system call
360404
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
361-
if unit_file_state == "enabled":
405+
if unit_file_state == "enabled" or not unit_file_state:
362406
continue
363407

364408
for suffix in feature_suffixes:
@@ -388,7 +432,7 @@ class FeatureHandler(object):
388432
for feature_name in feature_names:
389433
# Check if it is already disabled, if yes skip the system call
390434
unit_file_state = self.get_systemd_unit_state("{}.{}".format(feature_name, feature_suffixes[-1]))
391-
if unit_file_state in ("disabled", "masked"):
435+
if unit_file_state in ("disabled", "masked") or not unit_file_state:
392436
continue
393437

394438
for suffix in reversed(feature_suffixes):

0 commit comments

Comments
 (0)