From 1d2b20ae6bf54924cdbb1d6607ea3e6296eafa6b Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak Date: Mon, 26 Oct 2020 03:08:08 +0200 Subject: [PATCH 01/13] [hostcfgd] Configure service auto-restart in hostcfgd. Before this change, a process runnning inside every SONiC container dealt with FEATURE table 'auto_restart' field and depending on the value decided wether a container has to be killed or not. If killed service auto restart mechanism restarts the container. This change moves the logic from container to the host daemon - hostcfgd. * hostcfgd refactoring - move feature handling in another class. * override systemd service Restart= setting from hostcfgd. * remove code that deals with FEATURE table from supervisor-proc-exit-listener. * remove default systemd Restart=always. Signed-off-by: Stepan Blyshchak --- dockers/docker-database/supervisord.conf.j2 | 2 +- .../docker-dhcp-relay.supervisord.conf.j2 | 2 +- .../frr/supervisord/supervisord.conf.j2 | 2 +- dockers/docker-fpm-gobgp/supervisord.conf | 2 +- dockers/docker-fpm-quagga/supervisord.conf | 2 +- dockers/docker-lldp/supervisord.conf.j2 | 2 +- dockers/docker-nat/supervisord.conf | 2 +- dockers/docker-orchagent/supervisord.conf | 2 +- .../docker-pmon.supervisord.conf.j2 | 2 +- ...cker-router-advertiser.supervisord.conf.j2 | 2 +- dockers/docker-sflow/supervisord.conf | 2 +- dockers/docker-snmp/supervisord.conf | 2 +- dockers/docker-sonic-restapi/supervisord.conf | 2 +- .../docker-sonic-telemetry/supervisord.conf | 2 +- dockers/docker-teamd/supervisord.conf | 2 +- files/build_templates/dhcp_relay.service.j2 | 1 - files/build_templates/nat.service.j2 | 1 - .../per_namespace/bgp.service.j2 | 1 - .../per_namespace/database.service.j2 | 1 - .../per_namespace/lldp.service.j2 | 1 - .../per_namespace/swss.service.j2 | 1 - .../per_namespace/teamd.service.j2 | 1 - files/build_templates/pmon.service.j2 | 1 - files/build_templates/radv.service.j2 | 1 - files/build_templates/restapi.service.j2 | 1 - files/build_templates/sflow.service.j2 | 1 - .../share_image/database.service.j2 | 1 - files/build_templates/snmp.service.j2 | 1 - files/build_templates/telemetry.service.j2 | 1 - files/scripts/supervisor-proc-exit-listener | 40 +-- .../docker-syncd-bfn/supervisord.conf | 2 +- .../docker-syncd-brcm/supervisord.conf | 2 +- .../cavium/docker-syncd-cavm/supervisord.conf | 2 +- .../docker-syncd-centec/supervisord.conf | 2 +- .../docker-syncd-centec/supervisord.conf | 2 +- .../docker-syncd-mrvl/supervisord.conf | 2 +- .../docker-syncd-mrvl/supervisord.conf | 2 +- .../docker-syncd-mrvl/supervisord.conf | 2 +- .../docker-syncd-mlnx/supervisord.conf | 2 +- .../docker-syncd-nephos/supervisord.conf | 2 +- .../vs/docker-gbsyncd-vs/supervisord.conf | 2 +- platform/vs/docker-syncd-vs/supervisord.conf | 2 +- .../py2/docker-dhcp-relay.supervisord.conf | 2 +- .../py3/docker-dhcp-relay.supervisord.conf | 2 +- src/sonic-host-services/scripts/hostcfgd | 238 ++++++++++-------- 45 files changed, 172 insertions(+), 178 deletions(-) diff --git a/dockers/docker-database/supervisord.conf.j2 b/dockers/docker-database/supervisord.conf.j2 index 97b8a48225..8f581a0110 100644 --- a/dockers/docker-database/supervisord.conf.j2 +++ b/dockers/docker-database/supervisord.conf.j2 @@ -4,7 +4,7 @@ logfile_backups=2 nodaemon=true [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name database +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 b/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 index 9e8c553b65..c69f74152c 100644 --- a/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 +++ b/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=50 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name dhcp_relay +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-fpm-frr/frr/supervisord/supervisord.conf.j2 b/dockers/docker-fpm-frr/frr/supervisord/supervisord.conf.j2 index 9c379404bc..72494e1242 100644 --- a/dockers/docker-fpm-frr/frr/supervisord/supervisord.conf.j2 +++ b/dockers/docker-fpm-frr/frr/supervisord/supervisord.conf.j2 @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=50 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name bgp +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-fpm-gobgp/supervisord.conf b/dockers/docker-fpm-gobgp/supervisord.conf index b814dc024f..ddd2d9993c 100644 --- a/dockers/docker-fpm-gobgp/supervisord.conf +++ b/dockers/docker-fpm-gobgp/supervisord.conf @@ -4,7 +4,7 @@ logfile_backups=2 nodaemon=true [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name bgp +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-fpm-quagga/supervisord.conf b/dockers/docker-fpm-quagga/supervisord.conf index 7397a7428a..63d27a80ce 100644 --- a/dockers/docker-fpm-quagga/supervisord.conf +++ b/dockers/docker-fpm-quagga/supervisord.conf @@ -4,7 +4,7 @@ logfile_backups=2 nodaemon=true [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name bgp +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-lldp/supervisord.conf.j2 b/dockers/docker-lldp/supervisord.conf.j2 index 5499e2af46..6eefb2d4bf 100644 --- a/dockers/docker-lldp/supervisord.conf.j2 +++ b/dockers/docker-lldp/supervisord.conf.j2 @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name lldp +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-nat/supervisord.conf b/dockers/docker-nat/supervisord.conf index 9840300ec1..8c054763b4 100644 --- a/dockers/docker-nat/supervisord.conf +++ b/dockers/docker-nat/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name nat +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-orchagent/supervisord.conf b/dockers/docker-orchagent/supervisord.conf index 780a4d7904..f9b85009c7 100644 --- a/dockers/docker-orchagent/supervisord.conf +++ b/dockers/docker-orchagent/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=100 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name swss +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 b/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 index c44bbbbf8e..ecd295a213 100644 --- a/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 +++ b/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=100 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name pmon +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-router-advertiser/docker-router-advertiser.supervisord.conf.j2 b/dockers/docker-router-advertiser/docker-router-advertiser.supervisord.conf.j2 index 5a6416b0ff..b3969040d1 100644 --- a/dockers/docker-router-advertiser/docker-router-advertiser.supervisord.conf.j2 +++ b/dockers/docker-router-advertiser/docker-router-advertiser.supervisord.conf.j2 @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-script] -command=/usr/bin/supervisor-proc-exit-listener --container-name radv +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-sflow/supervisord.conf b/dockers/docker-sflow/supervisord.conf index 2731d89d06..b90d3ea40b 100644 --- a/dockers/docker-sflow/supervisord.conf +++ b/dockers/docker-sflow/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name sflow +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-snmp/supervisord.conf b/dockers/docker-snmp/supervisord.conf index 05f54122b5..dcd47a8c8b 100644 --- a/dockers/docker-snmp/supervisord.conf +++ b/dockers/docker-snmp/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=50 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name snmp +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/dockers/docker-sonic-restapi/supervisord.conf b/dockers/docker-sonic-restapi/supervisord.conf index ac9a9fe18a..ea95d3fbb2 100644 --- a/dockers/docker-sonic-restapi/supervisord.conf +++ b/dockers/docker-sonic-restapi/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name restapi +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=false diff --git a/dockers/docker-sonic-telemetry/supervisord.conf b/dockers/docker-sonic-telemetry/supervisord.conf index f0578803a2..3f36dae111 100644 --- a/dockers/docker-sonic-telemetry/supervisord.conf +++ b/dockers/docker-sonic-telemetry/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=50 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name telemetry +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=false diff --git a/dockers/docker-teamd/supervisord.conf b/dockers/docker-teamd/supervisord.conf index c152932608..3c5103e25d 100644 --- a/dockers/docker-teamd/supervisord.conf +++ b/dockers/docker-teamd/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=50 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name teamd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/files/build_templates/dhcp_relay.service.j2 b/files/build_templates/dhcp_relay.service.j2 index d501a663fe..6e814f28fc 100644 --- a/files/build_templates/dhcp_relay.service.j2 +++ b/files/build_templates/dhcp_relay.service.j2 @@ -11,7 +11,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/bin/{{ docker_container_name }}.sh start ExecStart=/usr/bin/{{ docker_container_name }}.sh wait ExecStop=/usr/bin/{{ docker_container_name }}.sh stop -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/nat.service.j2 b/files/build_templates/nat.service.j2 index 1d267cfe93..6c691382d9 100644 --- a/files/build_templates/nat.service.j2 +++ b/files/build_templates/nat.service.j2 @@ -11,7 +11,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/bin/{{docker_container_name}}.sh start ExecStart=/usr/bin/{{docker_container_name}}.sh wait ExecStop=/usr/bin/{{docker_container_name}}.sh stop -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/per_namespace/bgp.service.j2 b/files/build_templates/per_namespace/bgp.service.j2 index 9f3c72e20d..2236ed3655 100644 --- a/files/build_templates/per_namespace/bgp.service.j2 +++ b/files/build_templates/per_namespace/bgp.service.j2 @@ -14,7 +14,6 @@ ExecStartPre=/usr/local/bin/{{docker_container_name}}.sh start{% if multi_instan ExecStart=/usr/local/bin/{{docker_container_name}}.sh wait{% if multi_instance == 'true' %} %i{% endif %} ExecStop=/usr/local/bin/{{docker_container_name}}.sh stop{% if multi_instance == 'true' %} %i{% endif %} -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/per_namespace/database.service.j2 b/files/build_templates/per_namespace/database.service.j2 index c8a59ab25b..c42995d205 100644 --- a/files/build_templates/per_namespace/database.service.j2 +++ b/files/build_templates/per_namespace/database.service.j2 @@ -17,7 +17,6 @@ User=root ExecStartPre=/usr/bin/{{docker_container_name}}.sh start{% if multi_instance == 'true' %} %i{% endif %} ExecStart=/usr/bin/{{docker_container_name}}.sh wait{% if multi_instance == 'true' %} %i{% endif %} ExecStop=/usr/bin/{{docker_container_name}}.sh stop{% if multi_instance == 'true' %} %i{% endif %} -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/per_namespace/lldp.service.j2 b/files/build_templates/per_namespace/lldp.service.j2 index b48675b032..6ecde25ef5 100644 --- a/files/build_templates/per_namespace/lldp.service.j2 +++ b/files/build_templates/per_namespace/lldp.service.j2 @@ -15,7 +15,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/bin/{{docker_container_name}}.sh start{% if multi_instance == 'true' %} %i{% endif %} ExecStart=/usr/bin/{{docker_container_name}}.sh wait{% if multi_instance == 'true' %} %i{% endif %} ExecStop=/usr/bin/{{docker_container_name}}.sh stop{% if multi_instance == 'true' %} %i{% endif %} -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/per_namespace/swss.service.j2 b/files/build_templates/per_namespace/swss.service.j2 index 352d1593a6..0978c11da6 100644 --- a/files/build_templates/per_namespace/swss.service.j2 +++ b/files/build_templates/per_namespace/swss.service.j2 @@ -22,7 +22,6 @@ Environment=sonic_asic_platform={{ sonic_asic_platform }} ExecStartPre=/usr/local/bin/swss.sh start{% if multi_instance == 'true' %} %i{% endif %} ExecStart=/usr/local/bin/swss.sh wait{% if multi_instance == 'true' %} %i{% endif %} ExecStop=/usr/local/bin/swss.sh stop{% if multi_instance == 'true' %} %i{% endif %} -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/per_namespace/teamd.service.j2 b/files/build_templates/per_namespace/teamd.service.j2 index 322ffdc407..5827185db0 100644 --- a/files/build_templates/per_namespace/teamd.service.j2 +++ b/files/build_templates/per_namespace/teamd.service.j2 @@ -12,7 +12,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/local/bin/{{docker_container_name}}.sh start{% if multi_instance == 'true' %} %i{% endif %} ExecStart=/usr/local/bin/{{docker_container_name}}.sh wait{% if multi_instance == 'true' %} %i{% endif %} ExecStop=/usr/local/bin/{{docker_container_name}}.sh stop{% if multi_instance == 'true' %} %i{% endif %} -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/pmon.service.j2 b/files/build_templates/pmon.service.j2 index 9195b4d38a..0e6f7a73a7 100644 --- a/files/build_templates/pmon.service.j2 +++ b/files/build_templates/pmon.service.j2 @@ -14,7 +14,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/bin/{{docker_container_name}}.sh start ExecStart=/usr/bin/{{docker_container_name}}.sh wait ExecStop=/usr/bin/{{docker_container_name}}.sh stop -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/radv.service.j2 b/files/build_templates/radv.service.j2 index 5cf25a2104..d785d317ce 100644 --- a/files/build_templates/radv.service.j2 +++ b/files/build_templates/radv.service.j2 @@ -11,7 +11,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/local/bin/{{ docker_container_name }}.sh start ExecStart=/usr/local/bin/{{ docker_container_name }}.sh wait ExecStop=/usr/local/bin/{{ docker_container_name }}.sh stop -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/restapi.service.j2 b/files/build_templates/restapi.service.j2 index df1a50eb56..bcea62dcb1 100644 --- a/files/build_templates/restapi.service.j2 +++ b/files/build_templates/restapi.service.j2 @@ -9,7 +9,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/bin/{{docker_container_name}}.sh start ExecStart=/usr/bin/{{docker_container_name}}.sh wait ExecStop=/usr/bin/{{docker_container_name}}.sh stop -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/sflow.service.j2 b/files/build_templates/sflow.service.j2 index 643bf64696..fcfc60fa91 100644 --- a/files/build_templates/sflow.service.j2 +++ b/files/build_templates/sflow.service.j2 @@ -11,7 +11,6 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/bin/{{docker_container_name}}.sh start ExecStart=/usr/bin/{{docker_container_name}}.sh wait ExecStop=/usr/bin/{{docker_container_name}}.sh stop -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/share_image/database.service.j2 b/files/build_templates/share_image/database.service.j2 index cc3f1b0a43..c1b49eecf0 100644 --- a/files/build_templates/share_image/database.service.j2 +++ b/files/build_templates/share_image/database.service.j2 @@ -13,7 +13,6 @@ User=root ExecStartPre=/usr/bin/{{docker_container_name}}.sh start chassisdb ExecStart=/usr/bin/{{docker_container_name}}.sh wait chassisdb ExecStop=/usr/bin/{{docker_container_name}}.sh stop chassisdb -Restart=always RestartSec=30 [Install] diff --git a/files/build_templates/snmp.service.j2 b/files/build_templates/snmp.service.j2 index 4997ab737e..bb8a0a825f 100644 --- a/files/build_templates/snmp.service.j2 +++ b/files/build_templates/snmp.service.j2 @@ -12,6 +12,5 @@ StartLimitBurst=3 ExecStartPre=/usr/bin/{{docker_container_name}}.sh start ExecStart=/usr/bin/{{docker_container_name}}.sh wait ExecStop=/usr/bin/{{docker_container_name}}.sh stop -Restart=always RestartSec=30 diff --git a/files/build_templates/telemetry.service.j2 b/files/build_templates/telemetry.service.j2 index 43fa039156..d033b84476 100644 --- a/files/build_templates/telemetry.service.j2 +++ b/files/build_templates/telemetry.service.j2 @@ -11,6 +11,5 @@ User={{ sonicadmin_user }} ExecStartPre=/usr/bin/{{docker_container_name}}.sh start ExecStart=/usr/bin/{{docker_container_name}}.sh wait ExecStop=/usr/bin/{{docker_container_name}}.sh stop -Restart=always RestartSec=30 diff --git a/files/scripts/supervisor-proc-exit-listener b/files/scripts/supervisor-proc-exit-listener index e81e6b31a8..c82605b39e 100755 --- a/files/scripts/supervisor-proc-exit-listener +++ b/files/scripts/supervisor-proc-exit-listener @@ -6,8 +6,6 @@ import signal import sys import syslog -import swsssdk - from supervisor import childutils # Each line of this file should specify either one critical process or one @@ -46,16 +44,6 @@ def get_critical_group_and_process_list(): return critical_group_list, critical_process_list def main(argv): - container_name = None - opts, args = getopt.getopt(argv, "c:", ["container-name="]) - for opt, arg in opts: - if opt in ("-c", "--container-name"): - container_name = arg - - if not container_name: - syslog.syslog(syslog.LOG_ERR, "Container name not specified. Exiting...") - sys.exit(1) - critical_group_list, critical_process_list = get_critical_group_and_process_list() while True: @@ -73,32 +61,14 @@ def main(argv): if headers['eventname'] == 'PROCESS_STATE_EXITED': payload_headers, payload_data = childutils.eventdata(payload + '\n') - expected = int(payload_headers['expected']) + expected_exit = int(payload_headers['expected']) processname = payload_headers['processname'] groupname = payload_headers['groupname'] - # Read the status of auto-restart feature from Config_DB. - if container_name != 'database': - config_db = swsssdk.ConfigDBConnector() - config_db.connect() - features_table = config_db.get_table(FEATURE_TABLE_NAME) - if not features_table: - syslog.syslog(syslog.LOG_ERR, "Unable to retrieve features table from Config DB. Exiting...") - sys.exit(2) - - if not features_table.has_key(container_name): - syslog.syslog(syslog.LOG_ERR, "Unable to retrieve feature '{}'. Exiting...".format(container_name)) - sys.exit(3) - - restart_feature = features_table[container_name].get('auto_restart') - if not restart_feature: - syslog.syslog(syslog.LOG_ERR, "Unable to determine auto-restart feature status for '{}'. Exiting...".format(container_name)) - sys.exit(4) - - # If container is database or auto-restart feature is enabled and at the same time - # a critical process exited unexpectedly, terminate supervisor - if ((container_name == 'database' or restart_feature == 'enabled') and expected == 0 and - (processname in critical_process_list or groupname in critical_group_list)): + is_critical = processname in critical_process_list or groupname in critical_group_list + + # If a critical process exited unexpectedly, terminate supervisor + if is_critical and not expected_exit: MSG_FORMAT_STR = "Process {} exited unxepectedly. Terminating supervisor..." msg = MSG_FORMAT_STR.format(payload_headers['processname']) syslog.syslog(syslog.LOG_INFO, msg) diff --git a/platform/barefoot/docker-syncd-bfn/supervisord.conf b/platform/barefoot/docker-syncd-bfn/supervisord.conf index 7137279103..f32c116969 100644 --- a/platform/barefoot/docker-syncd-bfn/supervisord.conf +++ b/platform/barefoot/docker-syncd-bfn/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/broadcom/docker-syncd-brcm/supervisord.conf b/platform/broadcom/docker-syncd-brcm/supervisord.conf index 77f6519c2e..17d1bcfa7c 100644 --- a/platform/broadcom/docker-syncd-brcm/supervisord.conf +++ b/platform/broadcom/docker-syncd-brcm/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/cavium/docker-syncd-cavm/supervisord.conf b/platform/cavium/docker-syncd-cavm/supervisord.conf index 0c6285d46a..c823ab5680 100644 --- a/platform/cavium/docker-syncd-cavm/supervisord.conf +++ b/platform/cavium/docker-syncd-cavm/supervisord.conf @@ -4,7 +4,7 @@ logfile_backups=2 nodaemon=true [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/centec-arm64/docker-syncd-centec/supervisord.conf b/platform/centec-arm64/docker-syncd-centec/supervisord.conf index 1a15c140a7..d78fb1da68 100755 --- a/platform/centec-arm64/docker-syncd-centec/supervisord.conf +++ b/platform/centec-arm64/docker-syncd-centec/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/centec/docker-syncd-centec/supervisord.conf b/platform/centec/docker-syncd-centec/supervisord.conf index dc6977ed47..14a0fb1efc 100644 --- a/platform/centec/docker-syncd-centec/supervisord.conf +++ b/platform/centec/docker-syncd-centec/supervisord.conf @@ -12,7 +12,7 @@ exitcodes=0,3 events=PROCESS_STATE [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/marvell-arm64/docker-syncd-mrvl/supervisord.conf b/platform/marvell-arm64/docker-syncd-mrvl/supervisord.conf index 1a15c140a7..d78fb1da68 100644 --- a/platform/marvell-arm64/docker-syncd-mrvl/supervisord.conf +++ b/platform/marvell-arm64/docker-syncd-mrvl/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/marvell-armhf/docker-syncd-mrvl/supervisord.conf b/platform/marvell-armhf/docker-syncd-mrvl/supervisord.conf index 408899f888..3bbfc0be61 100644 --- a/platform/marvell-armhf/docker-syncd-mrvl/supervisord.conf +++ b/platform/marvell-armhf/docker-syncd-mrvl/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/marvell/docker-syncd-mrvl/supervisord.conf b/platform/marvell/docker-syncd-mrvl/supervisord.conf index 73523ac02a..3535f54f5f 100644 --- a/platform/marvell/docker-syncd-mrvl/supervisord.conf +++ b/platform/marvell/docker-syncd-mrvl/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/mellanox/docker-syncd-mlnx/supervisord.conf b/platform/mellanox/docker-syncd-mlnx/supervisord.conf index 55578ff2dc..b2f1434eae 100644 --- a/platform/mellanox/docker-syncd-mlnx/supervisord.conf +++ b/platform/mellanox/docker-syncd-mlnx/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/nephos/docker-syncd-nephos/supervisord.conf b/platform/nephos/docker-syncd-nephos/supervisord.conf index 55578ff2dc..b2f1434eae 100644 --- a/platform/nephos/docker-syncd-nephos/supervisord.conf +++ b/platform/nephos/docker-syncd-nephos/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/vs/docker-gbsyncd-vs/supervisord.conf b/platform/vs/docker-gbsyncd-vs/supervisord.conf index 6ba7ecbbdd..dbcb8f96e5 100644 --- a/platform/vs/docker-gbsyncd-vs/supervisord.conf +++ b/platform/vs/docker-gbsyncd-vs/supervisord.conf @@ -12,7 +12,7 @@ exitcodes=0,3 events=PROCESS_STATE [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name gbsyncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/platform/vs/docker-syncd-vs/supervisord.conf b/platform/vs/docker-syncd-vs/supervisord.conf index 36e33850cb..d0031f1e04 100644 --- a/platform/vs/docker-syncd-vs/supervisord.conf +++ b/platform/vs/docker-syncd-vs/supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=25 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name syncd +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/src/sonic-config-engine/tests/sample_output/py2/docker-dhcp-relay.supervisord.conf b/src/sonic-config-engine/tests/sample_output/py2/docker-dhcp-relay.supervisord.conf index be52694b78..c9f6296428 100644 --- a/src/sonic-config-engine/tests/sample_output/py2/docker-dhcp-relay.supervisord.conf +++ b/src/sonic-config-engine/tests/sample_output/py2/docker-dhcp-relay.supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=50 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name dhcp_relay +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/src/sonic-config-engine/tests/sample_output/py3/docker-dhcp-relay.supervisord.conf b/src/sonic-config-engine/tests/sample_output/py3/docker-dhcp-relay.supervisord.conf index 3e485f4ddc..b376097296 100644 --- a/src/sonic-config-engine/tests/sample_output/py3/docker-dhcp-relay.supervisord.conf +++ b/src/sonic-config-engine/tests/sample_output/py3/docker-dhcp-relay.supervisord.conf @@ -13,7 +13,7 @@ events=PROCESS_STATE buffer_size=50 [eventlistener:supervisor-proc-exit-listener] -command=/usr/bin/supervisor-proc-exit-listener --container-name dhcp_relay +command=/usr/bin/supervisor-proc-exit-listener events=PROCESS_STATE_EXITED autostart=true autorestart=unexpected diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index b97d800db3..59c143fd6a 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -42,6 +42,139 @@ def obfuscate(data): return data +class Feature(object): + """ Represents a feature configuration from CONFIG DB data. """ + + def __init__(self, feature_name, feature_cfg): + self.name = feature_name + self.state = feature_cfg.get('state') + self.auto_restart = feature_cfg.get('auto_restart', 'disabled') + self.has_timer = ast.literal_eval(feature_cfg.get('has_timer', 'False')) + self.has_global_scope = ast.literal_eval(feature_cfg.get('has_global_scope', 'True')) + self.has_per_asic_scope = ast.literal_eval(feature_cfg.get('has_per_asic_scope', 'False')) + + +class FeatureHandler(object): + """ Handles FEATURE table updates. """ + + SYSTEMD_SERVICE_CONF_D = '/etc/systemd/system/{}.service.d/' + + def __init__(self, config_db): + self._config_db = config_db + self._cached_config = {} + self.is_multi_npu = device_info.is_multi_npu() + + def handle(self, feature_name, feature_cfg): + feature = Feature(feature_name, feature_cfg) + if feature.state is None: + syslog.syslog(syslog.LOG_WARNING, "Enable state of feature '{}' is None".format(feature_name)) + return + + self._cached_config.setdefault(feature_name, Feature(feature_name, {})) + + # Change auto-restart configuration first. + # If service reached failed state before this configuration applies (e.g. on boot) + # the next called self.update_feature_state will start it again. If it will fail + # again the auto restart will kick-in. Another order may leave it in failed state + # and not auto restart. + if self._cached_config[feature_name].auto_restart != feature.auto_restart: + self._cached_config[feature_name].auto_restart = feature.auto_restart + self.update_feature_auto_restart(feature) + + # Enable/disable the container service if the feature state was changed from its previous state. + if self._cached_config[feature_name].state != feature.state: + self._cached_config[feature_name].state = feature.state + self.update_feature_state(feature) + + def update_all_features_config(self): + feature_table = self._config_db.get_table('FEATURE') + for feature_name in feature_table.keys(): + if not feature_name: + syslog.syslog(syslog.LOG_WARNING, "Feature is None") + continue + + self.handle(feature_name, feature_table[feature_name]) + + def update_feature_state(self, feature): + # Create feature name suffix depending feature is running in host or namespace or in both + feature_name_suffix_list = self._get_feature_name_suffixes(feature) + + if not feature_name_suffix_list: + syslog.syslog(syslog.LOG_ERR, "Feature '{}' service not available" + .format(feature.name)) + + feature_suffixes = self._get_feature_suffixes(feature) + + if feature.state == "enabled": + start_cmds = [] + for feature_name_suffix in feature_name_suffix_list: + for suffix in feature_suffixes: + start_cmds.append("sudo systemctl unmask {}.{}".format(feature_name_suffix, suffix)) + # If feature has timer associated with it, start/enable corresponding systemd .timer unit + # otherwise, start/enable corresponding systemd .service unit + start_cmds.append("sudo systemctl enable {}.{}".format(feature_name_suffix, feature_suffixes[-1])) + start_cmds.append("sudo systemctl start {}.{}".format(feature_name_suffix, feature_suffixes[-1])) + for cmd in start_cmds: + syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd)) + try: + subprocess.check_call(cmd, shell=True) + except subprocess.CalledProcessError as err: + syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" + .format(err.cmd, err.returncode, err.output)) + syslog.syslog(syslog.LOG_ERR, "Feature '{}.{}' failed to be enabled and started" + .format(feature.name, feature_suffixes[-1])) + return + syslog.syslog(syslog.LOG_INFO, "Feature '{}.{}' is enabled and started" + .format(feature.name, feature_suffixes[-1])) + elif feature.state == "disabled": + stop_cmds = [] + for feature_name_suffix in feature_name_suffix_list: + for suffix in reversed(feature_suffixes): + stop_cmds.append("sudo systemctl stop {}.{}".format(feature_name_suffix, suffix)) + stop_cmds.append("sudo systemctl disable {}.{}".format(feature_name_suffix, suffix)) + stop_cmds.append("sudo systemctl mask {}.{}".format(feature_name_suffix, suffix)) + for cmd in stop_cmds: + syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd)) + try: + subprocess.check_call(cmd, shell=True) + except subprocess.CalledProcessError as err: + syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" + .format(err.cmd, err.returncode, err.output)) + syslog.syslog(syslog.LOG_ERR, "Feature '{}' failed to be stopped " + "and disabled".format(feature.name)) + return + syslog.syslog(syslog.LOG_INFO, "Feature '{}' is stopped and disabled".format(feature.name)) + else: + syslog.syslog(syslog.LOG_ERR, "Unexpected state value '{}' for feature '{}'" + .format(feature.state, feature.name)) + + def update_feature_auto_restart(self, feature): + restart_config = "always" if feature.auto_restart == "enabled" else "no" + service_conf = "[Service]\nRestart={}\n".format(restart_config) + feature_name_suffix_list = self._get_feature_name_suffixes(feature) + for service_name in feature_name_suffix_list: + dir_name = self.SYSTEMD_SERVICE_CONF_D.format(service_name) + if not os.path.exists(dir_name): + os.mkdir(dir_name) + with open(os.path.join(dir_name, 'auto_restart.conf'), 'w') as cfgfile: + cfgfile.write(service_conf) + + try: + subprocess.check_call("sudo systemctl daemon-reload", shell=True) + except subprocess.CalledProcessError as err: + syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" + .format(err.cmd, err.returncode, err.output)) + syslog.syslog(syslog.LOG_ERR, "Feature '{}' failed to configure auto_restart".format(feature.name)) + return + + def _get_feature_name_suffixes(self, feature): + return (([feature.name] if feature.has_global_scope or not self.is_multi_npu else []) + + ([(feature.name + '@' + str(asic_inst)) for asic_inst in range(device_info.get_num_npus()) + if feature.has_per_asic_scope and self.is_multi_npu])) + + def _get_feature_suffixes(self, feature): + return ["service"] + (["timer"] if feature.has_timer else []) + class Iptables(object): def __init__(self): @@ -235,8 +368,7 @@ class HostConfigDaemon: self.aaacfg = AaaCfg() self.iptables = Iptables() - # Cache the values of 'state' field in 'FEATURE' table of each container - self.cached_feature_states = {} + self.feature = FeatureHandler(self.config_db) self.is_multi_npu = device_info.is_multi_npu() @@ -250,83 +382,6 @@ class HostConfigDaemon: lpbk_table = self.config_db.get_table('LOOPBACK_INTERFACE') self.iptables.load(lpbk_table) - - def update_feature_state(self, feature_name, state, feature_table): - has_timer = ast.literal_eval(feature_table[feature_name].get('has_timer', 'False')) - has_global_scope = ast.literal_eval(feature_table[feature_name].get('has_global_scope', 'True')) - has_per_asic_scope = ast.literal_eval(feature_table[feature_name].get('has_per_asic_scope', 'False')) - - # Create feature name suffix depending feature is running in host or namespace or in both - feature_name_suffix_list = (([feature_name] if has_global_scope or not self.is_multi_npu else []) + - ([(feature_name + '@' + str(asic_inst)) for asic_inst in range(device_info.get_num_npus()) - if has_per_asic_scope and self.is_multi_npu])) - - if not feature_name_suffix_list: - syslog.syslog(syslog.LOG_ERR, "Feature '{}' service not available" - .format(feature_name)) - - feature_suffixes = ["service"] + (["timer"] if has_timer else []) - - if state == "enabled": - start_cmds = [] - for feature_name_suffix in feature_name_suffix_list: - for suffix in feature_suffixes: - start_cmds.append("sudo systemctl unmask {}.{}".format(feature_name_suffix, suffix)) - # If feature has timer associated with it, start/enable corresponding systemd .timer unit - # otherwise, start/enable corresponding systemd .service unit - start_cmds.append("sudo systemctl enable {}.{}".format(feature_name_suffix, feature_suffixes[-1])) - start_cmds.append("sudo systemctl start {}.{}".format(feature_name_suffix, feature_suffixes[-1])) - for cmd in start_cmds: - syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd)) - try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" - .format(err.cmd, err.returncode, err.output)) - syslog.syslog(syslog.LOG_ERR, "Feature '{}.{}' failed to be enabled and started" - .format(feature_name, feature_suffixes[-1])) - return - syslog.syslog(syslog.LOG_INFO, "Feature '{}.{}' is enabled and started" - .format(feature_name, feature_suffixes[-1])) - elif state == "disabled": - stop_cmds = [] - for feature_name_suffix in feature_name_suffix_list: - for suffix in reversed(feature_suffixes): - stop_cmds.append("sudo systemctl stop {}.{}".format(feature_name_suffix, suffix)) - stop_cmds.append("sudo systemctl disable {}.{}".format(feature_name_suffix, suffix)) - stop_cmds.append("sudo systemctl mask {}.{}".format(feature_name_suffix, suffix)) - for cmd in stop_cmds: - syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd)) - try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" - .format(err.cmd, err.returncode, err.output)) - syslog.syslog(syslog.LOG_ERR, "Feature '{}' failed to be stopped and disabled".format(feature_name)) - return - syslog.syslog(syslog.LOG_INFO, "Feature '{}' is stopped and disabled".format(feature_name)) - else: - syslog.syslog(syslog.LOG_ERR, "Unexpected state value '{}' for feature '{}'" - .format(state, feature_name)) - - - def update_all_feature_states(self): - feature_table = self.config_db.get_table('FEATURE') - for feature_name in feature_table: - if not feature_name: - syslog.syslog(syslog.LOG_WARNING, "Feature is None") - continue - - state = feature_table[feature_name]['state'] - if not state: - syslog.syslog(syslog.LOG_WARNING, "Eanble state of feature '{}' is None".format(feature_name)) - continue - - # Store the initial value of 'state' field in 'FEATURE' table of a specific container - self.cached_feature_states[feature_name] = state - - self.update_feature_state(feature_name, state, feature_table) - def aaa_handler(self, key, data): self.aaacfg.aaa_update(key, data) @@ -355,35 +410,18 @@ class HostConfigDaemon: self.iptables.iptables_handler(key, data, add) - def feature_state_handler(self, key, data): - feature_name = key - feature_table = self.config_db.get_table('FEATURE') - if feature_name not in feature_table: - syslog.syslog(syslog.LOG_WARNING, "Feature '{}' not in FEATURE table".format(feature_name)) - return - - state = feature_table[feature_name]['state'] - if not state: - syslog.syslog(syslog.LOG_WARNING, "Enable state of feature '{}' is None".format(feature_name)) - return - - self.cached_feature_states.setdefault(feature_name, 'disabled') - - # Enable/disable the container service if the feature state was changed from its previous state. - if self.cached_feature_states[feature_name] != state: - self.cached_feature_states[feature_name] = state - self.update_feature_state(feature_name, state, feature_table) + def feature_handler(self, key, data): + self.feature.handle(key, data) def start(self): - self.config_db.subscribe('AAA', lambda table, key, data: self.aaa_handler(key, data)) self.config_db.subscribe('TACPLUS_SERVER', lambda table, key, data: self.tacacs_server_handler(key, data)) self.config_db.subscribe('TACPLUS', lambda table, key, data: self.tacacs_global_handler(key, data)) self.config_db.subscribe('LOOPBACK_INTERFACE', lambda table, key, data: self.lpbk_handler(key, data)) - self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature_state_handler(key, data)) + self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature_handler(key, data)) # Update all feature states once upon starting - self.update_all_feature_states() + self.feature.update_all_features_config() # Defer load until subscribe self.load() From 6f47365ef3cef8319d320605a7705ec77716c6c0 Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak Date: Mon, 16 Nov 2020 23:32:10 +0200 Subject: [PATCH 02/13] add feature config parsing test Signed-off-by: Stepan Blyshchak --- .../tests/hostcfgd_test.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/sonic-host-services/tests/hostcfgd_test.py diff --git a/src/sonic-host-services/tests/hostcfgd_test.py b/src/sonic-host-services/tests/hostcfgd_test.py new file mode 100644 index 0000000000..e0e65441a5 --- /dev/null +++ b/src/sonic-host-services/tests/hostcfgd_test.py @@ -0,0 +1,47 @@ +import imp +import sys +import os +import pytest + +import swsssdk + +from .mock_connector import MockConnector + +swsssdk.SonicV2Connector = MockConnector + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, modules_path) + +imp.load_source('hostcfgd', scripts_path + '/hostcfgd') +from hostcfgd import * + +class TestHostConfigurationDaemon(object): + def test_feature_config_parsing(self): + swss_feature = Feature('swss', { + 'state': 'enabled', + 'auto_restart': 'enabled', + 'has_timer': 'True', + 'has_global_scope': 'False', + 'has_per_asic_scope': 'True', + }) + + assert swss_feature.name == 'swss' + assert swss_feature.state == 'enabled' + assert swss_feature.auto_restart == 'enabled' + assert swss_feature.has_timer + assert not swss_feature.has_global_scope + assert swss_feature.has_per_asic_scope + + def test_feature_config_parsing_defaults(self): + swss_feature = Feature('swss', { + 'state': 'enabled', + }) + + assert swss_feature.name == 'swss' + assert swss_feature.state == 'enabled' + assert swss_feature.auto_restart == 'disabled' + assert not swss_feature.has_timer + assert swss_feature.has_global_scope + assert not swss_feature.has_per_asic_scope From bfbcfa8e2271d7c3c554dad1f1f175037622fe75 Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak Date: Mon, 16 Nov 2020 23:54:08 +0200 Subject: [PATCH 03/13] [supervisor-proc-exit-listener] remove unused import Signed-off-by: Stepan Blyshchak --- files/scripts/supervisor-proc-exit-listener | 1 - 1 file changed, 1 deletion(-) diff --git a/files/scripts/supervisor-proc-exit-listener b/files/scripts/supervisor-proc-exit-listener index c82605b39e..12a73f8be8 100755 --- a/files/scripts/supervisor-proc-exit-listener +++ b/files/scripts/supervisor-proc-exit-listener @@ -1,6 +1,5 @@ #!/usr/bin/env python -import getopt import os import signal import sys From 285f7a3434354a605e4b32383c9ee443f99ca5e0 Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak Date: Wed, 20 Jan 2021 18:25:22 +0200 Subject: [PATCH 04/13] [hostcfgd] enhance run_cmd with ability to raise exception and reuse it in feature handler code Signed-off-by: Stepan Blyshchak --- src/sonic-host-services/scripts/hostcfgd | 35 ++++++++---------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index 327343e566..eac50a23be 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -23,13 +23,17 @@ TACPLUS_SERVER_PASSKEY_DEFAULT = "" TACPLUS_SERVER_TIMEOUT_DEFAULT = "5" TACPLUS_SERVER_AUTH_TYPE_DEFAULT = "pap" -def run_cmd(cmd, log_err = True): + +def run_cmd(cmd, log_err=True, raise_exception=False): try: - subprocess.check_call(cmd, shell = True) + subprocess.check_call(cmd, shell=True) except Exception as err: if log_err: syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}" .format(err.cmd, err.returncode, err.output)) + if raise_exception: + raise + def is_true(val): if val == 'True' or val == 'true': @@ -49,15 +53,6 @@ def obfuscate(data): return data -def run_cmd(cmd, log_err = True): - try: - subprocess.check_call(cmd, shell = True) - except Exception as err: - if log_err: - syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}" - .format(err.cmd, err.returncode, err.output)) - - class Feature(object): """ Represents a feature configuration from CONFIG DB data. """ @@ -141,10 +136,8 @@ class FeatureHandler(object): for cmd in start_cmds: syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd)) try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" - .format(err.cmd, err.returncode, err.output)) + run_cmd(cmd, raise_exception=True) + except Exception as err: syslog.syslog(syslog.LOG_ERR, "Feature '{}.{}' failed to be enabled and started" .format(feature.name, feature_suffixes[-1])) return @@ -160,10 +153,8 @@ class FeatureHandler(object): for cmd in stop_cmds: syslog.syslog(syslog.LOG_INFO, "Running cmd: '{}'".format(cmd)) try: - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError as err: - syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" - .format(err.cmd, err.returncode, err.output)) + run_cmd(cmd, raise_exception=True) + except Exception as err: syslog.syslog(syslog.LOG_ERR, "Feature '{}' failed to be stopped " "and disabled".format(feature.name)) return @@ -192,10 +183,8 @@ class FeatureHandler(object): cfgfile.write(service_conf) try: - subprocess.check_call("sudo systemctl daemon-reload", shell=True) - except subprocess.CalledProcessError as err: - syslog.syslog(syslog.LOG_ERR, "'{}' failed. RC: {}, output: {}" - .format(err.cmd, err.returncode, err.output)) + run_cmd("sudo systemctl daemon-reload", raise_exception=True) + except Exception as err: syslog.syslog(syslog.LOG_ERR, "Feature '{}' failed to configure auto_restart".format(feature.name)) return From 9a82a82c8ef22a00c31e2de6676a1943a7984f1f Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Sat, 30 Jan 2021 19:31:03 +0200 Subject: [PATCH 05/13] [hostcfgd] remove unneded feature_handle method Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index eac50a23be..c59f6565f3 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -589,9 +589,6 @@ class HostConfigDaemon: self.iptables.iptables_handler(key, data, add) self.ntpcfg.handle_ntp_source_intf_chg(key) - def feature_handler(self, key, data): - self.feature.handle(key, data) - def ntp_server_handler (self, key, data): syslog.syslog(syslog.LOG_INFO, 'NTP server handler...') ntp_server_db = self.config_db.get_table('NTP_SERVER') @@ -618,7 +615,7 @@ class HostConfigDaemon: self.config_db.subscribe('TACPLUS_SERVER', lambda table, key, data: self.tacacs_server_handler(key, data)) self.config_db.subscribe('TACPLUS', lambda table, key, data: self.tacacs_global_handler(key, data)) self.config_db.subscribe('LOOPBACK_INTERFACE', lambda table, key, data: self.lpbk_handler(key, data)) - self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature_handler(key, data)) + self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature.handle(key, data)) self.config_db.subscribe('NTP_SERVER', lambda table, key, data: self.ntp_server_handler(key, data)) self.config_db.subscribe('NTP', lambda table, key, data: self.ntp_global_handler(key, data)) self.config_db.subscribe('KDUMP', lambda table, key, data: self.kdump_handler(key, data)) From ce19fc09e5acb44c31fedc20c423d7c6430d3d2e Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Tue, 16 Mar 2021 15:37:12 +0000 Subject: [PATCH 06/13] remove duplicated test Signed-off-by: Stepan Blyschak --- .../tests/hostcfgd_test.py | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/sonic-host-services/tests/hostcfgd_test.py diff --git a/src/sonic-host-services/tests/hostcfgd_test.py b/src/sonic-host-services/tests/hostcfgd_test.py deleted file mode 100644 index e0e65441a5..0000000000 --- a/src/sonic-host-services/tests/hostcfgd_test.py +++ /dev/null @@ -1,47 +0,0 @@ -import imp -import sys -import os -import pytest - -import swsssdk - -from .mock_connector import MockConnector - -swsssdk.SonicV2Connector = MockConnector - -test_path = os.path.dirname(os.path.abspath(__file__)) -modules_path = os.path.dirname(test_path) -scripts_path = os.path.join(modules_path, "scripts") -sys.path.insert(0, modules_path) - -imp.load_source('hostcfgd', scripts_path + '/hostcfgd') -from hostcfgd import * - -class TestHostConfigurationDaemon(object): - def test_feature_config_parsing(self): - swss_feature = Feature('swss', { - 'state': 'enabled', - 'auto_restart': 'enabled', - 'has_timer': 'True', - 'has_global_scope': 'False', - 'has_per_asic_scope': 'True', - }) - - assert swss_feature.name == 'swss' - assert swss_feature.state == 'enabled' - assert swss_feature.auto_restart == 'enabled' - assert swss_feature.has_timer - assert not swss_feature.has_global_scope - assert swss_feature.has_per_asic_scope - - def test_feature_config_parsing_defaults(self): - swss_feature = Feature('swss', { - 'state': 'enabled', - }) - - assert swss_feature.name == 'swss' - assert swss_feature.state == 'enabled' - assert swss_feature.auto_restart == 'disabled' - assert not swss_feature.has_timer - assert swss_feature.has_global_scope - assert not swss_feature.has_per_asic_scope From 5dad36eedb71685681544fbe50d3f159a1de0318 Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Thu, 18 Mar 2021 18:23:57 +0200 Subject: [PATCH 07/13] [hostcfgd] support dynamically added features Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index 46a3696d3f..a390bf5ce5 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -106,6 +106,7 @@ class FeatureHandler(object): def handle(self, feature_name, feature_cfg): feature = Feature(feature_name, feature_cfg, self._device_config) + self._cached_config.setdefault(feature_name, Feature(feature_name, {})) # Change auto-restart configuration first. # If service reached failed state before this configuration applies (e.g. on boot) From f19542a62a720e793c610446dc0a9b020ee52198 Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Mon, 29 Mar 2021 15:23:01 +0300 Subject: [PATCH 08/13] [hostcfgd] change service_name -> feature_name Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index a390bf5ce5..0671601ea1 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -164,8 +164,8 @@ class FeatureHandler(object): service_conf = "[Service]\nRestart={}\n".format(restart_config) feature_names, feature_suffixes = self.get_feature_attribute(feature) - for service_name in feature_names: - dir_name = self.SYSTEMD_SERVICE_CONF_D.format(service_name) + for feature_name in feature_names: + dir_name = self.SYSTEMD_SERVICE_CONF_D.format(feature_name) if not os.path.exists(dir_name): os.mkdir(dir_name) with open(os.path.join(dir_name, 'auto_restart.conf'), 'w') as cfgfile: From 63abd6cc1389bbff684236bcbc8802734fd13ae9 Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Wed, 28 Apr 2021 13:27:09 +0300 Subject: [PATCH 09/13] bring back missing code Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index 301a1f023b..6be10085a1 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -856,6 +856,16 @@ class HostConfigDaemon: ntp_global = self.config_db.get_table('NTP') self.ntpcfg.load(ntp_global, ntp_server) + try: + dev_meta = self.config_db.get_table('DEVICE_METADATA') + if 'localhost' in dev_meta: + if 'hostname' in dev_meta['localhost']: + self.hostname_cache = dev_meta['localhost']['hostname'] + except Exception as e: + pass + # Update AAA with the hostname + self.aaacfg.hostname_update(self.hostname_cache) + def aaa_handler(self, key, data): self.aaacfg.aaa_update(key, data) From f9c53f150fda69d6dd5ef9c56ebae4d48f5c9791 Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Wed, 5 May 2021 13:13:09 +0300 Subject: [PATCH 10/13] fix review comments Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 16 ++++++++-------- .../tests/hostcfgd/hostcfgd_test.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index a5713c7ff6..257b5b78b3 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -72,15 +72,15 @@ def obfuscate(data): class Feature(object): - """ Represents a feature configuration from CONFIG DB data. """ + """ Represents a feature configuration from CONFIG_DB data. """ def __init__(self, feature_name, feature_cfg, device_config=None): - """ Initialize Feature object based on CONFIG DB data. + """ Initialize Feature object based on CONFIG_DB data. Args: feature_name (str): Feature name string - feature_cfg (dict): Feature CONFIG DB configuration - deviec_config (dict): DEVICE_METADATA section of CONFIG DB + feature_cfg (dict): Feature CONFIG_DB configuration + deviec_config (dict): DEVICE_METADATA section of CONFIG_DB """ self.name = feature_name @@ -94,8 +94,8 @@ class Feature(object): """ Returns the target state for the feature by rendering the state field as J2 template. Args: - state_configuration (str): State configuration from CONFIG DB - deviec_config (dict): DEVICE_METADATA section of CONFIG DB + state_configuration (str): State configuration from CONFIG_DB + deviec_config (dict): DEVICE_METADATA section of CONFIG_DB Returns: (str): Target feature state """ @@ -114,7 +114,7 @@ class FeatureHandler(object): """ Handles FEATURE table updates. """ SYSTEMD_SYSTEM_DIR = '/etc/systemd/system/' - SYSTEMD_SERVICE_CONF_D = os.path.join(SYSTEMD_SYSTEM_DIR, '{}.service.d/') + SYSTEMD_SERVICE_CONF_DIR = os.path.join(SYSTEMD_SYSTEM_DIR, '{}.service.d/') def __init__(self, config_db, device_config): self._config_db = config_db @@ -183,7 +183,7 @@ class FeatureHandler(object): feature_names, feature_suffixes = self.get_feature_attribute(feature) for feature_name in feature_names: - dir_name = self.SYSTEMD_SERVICE_CONF_D.format(feature_name) + dir_name = self.SYSTEMD_SERVICE_CONF_DIR.format(feature_name) if not os.path.exists(dir_name): os.mkdir(dir_name) with open(os.path.join(dir_name, 'auto_restart.conf'), 'w') as cfgfile: diff --git a/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py b/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py index 71315c4f23..c8585f7036 100644 --- a/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py +++ b/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py @@ -71,7 +71,7 @@ def __verify_fs(self, table): "enabled": "always", "disabled": "no", } - auto_restart_conf = os.path.join(hostcfgd.FeatureHandler.SYSTEMD_SERVICE_CONF_D, "auto_restart.conf") + auto_restart_conf = os.path.join(hostcfgd.FeatureHandler.SYSTEMD_SERVICE_CONF_DIR, "auto_restart.conf") for feature in table: auto_restart = table[feature].get("auto_restart", "disabled") From 774781db5992fcd9263de29b8c55af62da9e5376 Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Fri, 21 May 2021 17:43:28 +0300 Subject: [PATCH 11/13] fix feature states transitions Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 67 ++++++++++++++++-------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index 257b5b78b3..03f67eb71c 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -91,8 +91,8 @@ class Feature(object): self.has_per_asic_scope = ast.literal_eval(feature_cfg.get('has_per_asic_scope', 'False')) def _get_target_state(self, state_configuration, device_config): - """ Returns the target state for the feature by rendering the state field as J2 template. - + """ Returns the target state for the feature by rendering the state field as J2 template. + Args: state_configuration (str): State configuration from CONFIG_DB deviec_config (dict): DEVICE_METADATA section of CONFIG_DB @@ -123,6 +123,11 @@ class FeatureHandler(object): self.is_multi_npu = device_info.is_multi_npu() def handle(self, feature_name, feature_cfg): + if not feature_cfg: + self._cached_config.pop(feature_name) + syslog.syslog(syslog.LOG_INFO, "Deregistering feature {}".format(feature_name)) + return + feature = Feature(feature_name, feature_cfg, self._device_config) self._cached_config.setdefault(feature_name, Feature(feature_name, {})) @@ -137,8 +142,10 @@ class FeatureHandler(object): # Enable/disable the container service if the feature state was changed from its previous state. if self._cached_config[feature_name].state != feature.state: - self.update_feature_state(feature) - self._cached_config[feature_name].state = feature.state + if self.update_feature_state(feature): + self._cached_config[feature_name].state = feature.state + else: + self.resync_feature_state(self._cached_config[feature_name]) def update_all_features_config(self): feature_table = self._config_db.get_table('FEATURE') @@ -157,25 +164,43 @@ class FeatureHandler(object): def update_feature_state(self, feature): cached_feature = self._cached_config[feature.name] + enable = False + disable = False + + # Allowed transitions: + # None -> always_enabled + # -> always_disabled + # -> enabled + # -> disabled + # always_enabled -> always_disabled + # enabled -> disabled + # disabled -> enabled + if cached_feature.state is None: + enable = feature.state in ("always_enabled", "enabled") + disable = feature.state in ("always_disabled", "disabled") + elif cached_feature.state == ("always_enabled", "always_disabled"): + disable = feature.state == "always_disabled" + elif cached_feature.state in ("enabled", "disabled"): + enable = feature.state == "enabled" + disable = feature.state == "disabled" + else: + syslog.syslog(syslog.LOG_INFO, "Feature {} service is {}".format(feature.name, cached_feature.state)) + return False - if self.is_invariant_feature_state(cached_feature.state): - if cached_feature.state != feature.state: - syslog.syslog(syslog.LOG_INFO, "Feature {} service is {}".format(feature.name, feature.state)) - self.resync_feature_state(cached_feature) - if feature.state == 'always_disabled': - self.disable_feature(feature) - syslog.syslog(syslog.LOG_INFO, "Feature {} is stopped and disabled".format(feature.name)) - return + if not enable and not disable: + syslog.syslog(syslog.LOG_ERR, "Unexpected state value '{}' for feature {}" + .format(feature.state, feature.name)) + return False - if feature.state == "enabled": + if enable: self.enable_feature(feature) syslog.syslog(syslog.LOG_INFO, "Feature {} is enabled and started".format(feature.name)) - elif feature.state == "disabled": + + if disable: self.disable_feature(feature) syslog.syslog(syslog.LOG_INFO, "Feature {} is stopped and disabled".format(feature.name)) - else: - syslog.syslog(syslog.LOG_ERR, "Unexpected state value '{}' for feature {}" - .format(feature.state, feature.name)) + + return True def update_feature_auto_restart(self, feature): restart_config = "always" if feature.auto_restart == "enabled" else "no" @@ -196,10 +221,10 @@ class FeatureHandler(object): return def get_feature_attribute(self, feature): - # Create feature name suffix depending feature is running in host or namespace or in both + # Create feature name suffix depending feature is running in host or namespace or in both feature_names = ( ([feature.name] if feature.has_global_scope or not self.is_multi_npu else []) + - ([(feature.name + '@' + str(asic_inst)) for asic_inst in range(device_info.get_num_npus()) + ([(feature.name + '@' + str(asic_inst)) for asic_inst in range(device_info.get_num_npus()) if feature.has_per_asic_scope and self.is_multi_npu]) ) @@ -211,9 +236,6 @@ class FeatureHandler(object): return feature_names, feature_suffixes - def is_invariant_feature_state(self, state): - return state in ('always_enabled', 'always_disabled') - def enable_feature(self, feature): cmds = [] feature_names, feature_suffixes = self.get_feature_attribute(feature) @@ -990,4 +1012,3 @@ def main(): if __name__ == "__main__": main() - From 1c6472c4e7da9d818516c66d3bb8a355aac4af83 Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Sat, 22 May 2021 03:06:41 +0300 Subject: [PATCH 12/13] fix condition Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index 03f67eb71c..1e19c6b024 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -178,7 +178,7 @@ class FeatureHandler(object): if cached_feature.state is None: enable = feature.state in ("always_enabled", "enabled") disable = feature.state in ("always_disabled", "disabled") - elif cached_feature.state == ("always_enabled", "always_disabled"): + elif cached_feature.state in ("always_enabled", "always_disabled"): disable = feature.state == "always_disabled" elif cached_feature.state in ("enabled", "disabled"): enable = feature.state == "enabled" From 32df167af7e5c494b4a8585abebbcd65f05ef0a3 Mon Sep 17 00:00:00 2001 From: Stepan Blyschak Date: Tue, 15 Jun 2021 11:06:45 +0000 Subject: [PATCH 13/13] rename feature -> feature_handler Signed-off-by: Stepan Blyschak --- src/sonic-host-services/scripts/hostcfgd | 6 +++--- src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sonic-host-services/scripts/hostcfgd b/src/sonic-host-services/scripts/hostcfgd index 1e19c6b024..8742e8908f 100755 --- a/src/sonic-host-services/scripts/hostcfgd +++ b/src/sonic-host-services/scripts/hostcfgd @@ -852,7 +852,7 @@ class HostConfigDaemon: self.hostname_cache="" self.aaacfg = AaaCfg() self.iptables = Iptables() - self.feature = FeatureHandler(self.config_db, self.device_config) + self.feature_handler = FeatureHandler(self.config_db, self.device_config) self.ntpcfg = NtpCfg(self.config_db) self.is_multi_npu = device_info.is_multi_npu() @@ -981,7 +981,7 @@ class HostConfigDaemon: self.config_db.subscribe('RADIUS', lambda table, key, data: self.radius_global_handler(key, data)) self.config_db.subscribe('MGMT_INTERFACE', lambda table, key, data: self.mgmt_intf_handler(key, data)) self.config_db.subscribe('LOOPBACK_INTERFACE', lambda table, key, data: self.lpbk_handler(key, data)) - self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature.handle(key, data)) + self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature_handler.handle(key, data)) self.config_db.subscribe('VLAN_INTERFACE', lambda table, key, data: self.vlan_intf_handler(key, data)) self.config_db.subscribe('VLAN_SUB_INTERFACE', lambda table, key, data: self.vlan_sub_intf_handler(key, data)) self.config_db.subscribe('PORTCHANNEL_INTERFACE', lambda table, key, data: self.portchannel_intf_handler(key, data)) @@ -997,7 +997,7 @@ class HostConfigDaemon: "systemctl has finished initialization -- proceeding ...") # Update all feature states once upon starting - self.feature.update_all_features_config() + self.feature_handler.update_all_features_config() # Defer load until subscribe self.load() diff --git a/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py b/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py index c8585f7036..7be9c40ec6 100644 --- a/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py +++ b/src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py @@ -98,7 +98,7 @@ def test_hostcfgd(self, test_name, test_data, fs): MockConfigDb.set_config_db(test_data["config_db"]) with mock.patch("hostcfgd.subprocess") as mocked_subprocess: host_config_daemon = hostcfgd.HostConfigDaemon() - host_config_daemon.feature.update_all_features_config() + host_config_daemon.feature_handler.update_all_features_config() assert self.__verify_table( MockConfigDb.get_config_db()["FEATURE"], test_data["expected_config_db"]["FEATURE"]