Skip to content
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
12 changes: 8 additions & 4 deletions device/nexthop/arm64-nexthop_b27-r0/pmon_daemon_control.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
{
"skip_thermalctld": false,
"skip_chassisd": true,
"skip_ledd": true,
"skip_xcvrd": true,
"skip_psud": true,
"skip_syseepromd": true,
"skip_pcied": true,
"skip_chassisd": true,
"skip_syseepromd": false,
"skip_thermalctld": false,
"skip_fancontrol": true,
"skip_sensors": true,
"skip_pcied": true,
"skip_stormond": false,
"skip_ycabled": true,
"include_sensormond": false,
"skip_chassis_db_init": false,
"thermalctld": {
"thermal_monitor_initial_interval": 5,
"thermal_monitor_update_interval": 60,
Expand Down
4 changes: 1 addition & 3 deletions dockers/docker-database/database_config.json.j2
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"persistence_for_warm_boot" : "yes"
}
{% endif %}
{% if DATABASE_TYPE is defined and (DATABASE_TYPE == "dpudb" or DATABASE_TYPE == "bmcdb") %}
{% if DATABASE_TYPE is defined and DATABASE_TYPE == "dpudb" %}
{% else %}
,
"redis_bmp":{
Expand Down Expand Up @@ -139,8 +139,6 @@
"separator": ":",
"instance" : {% if include_remote_db %} "remote_redis" {% else %} "redis" {% endif %}
}
{% elif DATABASE_TYPE is defined and DATABASE_TYPE == "bmcdb" %}
{# No BMP_STATE_DB for BMC devices #}
{% else %}
,
"BMP_STATE_DB" : {
Expand Down
4 changes: 0 additions & 4 deletions dockers/docker-database/docker-database-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ then
fi
fi

if [[ $IS_BMC_DEVICE == "true" ]]
then
export DATABASE_TYPE="bmcdb"
fi

export BMP_DB_PORT=6400

Expand Down
4 changes: 1 addition & 3 deletions dockers/docker-database/multi_database_config.json.j2
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"persistence_for_warm_boot" : "yes"
}
{% endif %}
{% if DATABASE_TYPE is defined and (DATABASE_TYPE == "dpudb" or DATABASE_TYPE == "bmcdb") %}
{% if DATABASE_TYPE is defined and DATABASE_TYPE == "dpudb" %}
{% else %}
,
"redis_bmp":{
Expand Down Expand Up @@ -185,8 +185,6 @@
"separator": ":",
"instance" : {% if include_remote_db %} "remote_redis" {% else %} "redis" {% endif %}
}
{% elif DATABASE_TYPE is defined and DATABASE_TYPE == "bmcdb" %}
{# No BMP_STATE_DB for BMC devices #}
{% else %}
,
"BMP_STATE_DB" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ stderr_syslog=true
dependent_startup=true


{% if IS_SWITCH_BMC %}
[program:bmcctld]
command=/usr/local/bin/bmcctld
priority=2
autostart=false
autorestart=unexpected
stdout_logfile=NONE
stdout_syslog=true
stderr_logfile=NONE
stderr_syslog=true
startsecs=10
startretries=50
dependent_startup=true
dependent_startup_wait_for=rsyslogd:running
{% endif %}


{% if delay_non_critical_daemon %}
[program:delay]
command=python3 /usr/bin/delay.py
Expand All @@ -40,7 +57,7 @@ startsecs=0
dependent_startup=true
{% endif %}

{% if not skip_chassisd and (IS_MODULAR_CHASSIS == 1 or is_smartswitch) %}
{% if not skip_chassisd and (IS_MODULAR_CHASSIS == 1 or is_smartswitch) and not IS_SWITCH_BMC %}
[program:chassisd]
command=/usr/local/bin/chassisd
priority=3
Expand Down
12 changes: 11 additions & 1 deletion dockers/docker-platform-monitor/docker_init.j2
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,17 @@ if [ -e $MODULAR_CHASSISDB_CONF_FILE ] && [[ $disaggregated_chassis -ne 1 ]]; th
IS_MODULAR_CHASSIS=1
fi

confvar="{\"HAVE_SENSORS_CONF\":$HAVE_SENSORS_CONF, \"HAVE_FANCONTROL_CONF\":$HAVE_FANCONTROL_CONF, \"API_VERSION\":$SONIC_PLATFORM_API_PYTHON_VERSION, \"IS_MODULAR_CHASSIS\":$IS_MODULAR_CHASSIS}"
# Determine if this pmon instance is running on the Switch BMC.
# switch_bmc=1 is set in platform_env.conf on BMC systems.
IS_SWITCH_BMC=0
if [ -e $PLATFORM_ENV_CONF_FILE ]; then
bmc_val=$(grep -s '^switch_bmc=' $PLATFORM_ENV_CONF_FILE | cut -d= -f2 | tr -d '[:space:]')
if [[ "$bmc_val" == "1" ]]; then
IS_SWITCH_BMC=1
fi
fi

confvar="{\"HAVE_SENSORS_CONF\":$HAVE_SENSORS_CONF, \"HAVE_FANCONTROL_CONF\":$HAVE_FANCONTROL_CONF, \"API_VERSION\":$SONIC_PLATFORM_API_PYTHON_VERSION, \"IS_MODULAR_CHASSIS\":$IS_MODULAR_CHASSIS, \"IS_SWITCH_BMC\":$IS_SWITCH_BMC}"

if [ -e $PMON_DAEMON_CONTROL_FILE ];
then
Expand Down
19 changes: 12 additions & 7 deletions files/build_templates/docker_image_ctl.j2
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ function preStartAction()
# Create an emtpy file and overwrite any RDB if already there
echo -n > /tmp/dump.rdb
docker cp /tmp/dump.rdb database$DEV:/var/lib/redis/
if [[ "$DATABASE_TYPE" != "bmcdb" ]]; then
docker cp /tmp/dump.rdb database$DEV:/var/lib/redis_bmp/
fi
docker cp /tmp/dump.rdb database$DEV:/var/lib/redis_bmp/
fi
fi
{%- elif docker_container_name == "pde" %}
Expand Down Expand Up @@ -433,6 +431,17 @@ start() {
source $PLATFORM_ENV_CONF
fi

# switch_host=1 / switch_bmc=1 may be set by platform_env.conf above.
# Normalise to 0 when absent so downstream env vars are always defined.
IS_SWITCH_HOST=${switch_host:-0}
IS_SWITCH_BMC=${switch_bmc:-0}

# Derive IS_BMC_DEVICE from switch_bmc so docker-database-init.sh can use
# the existing IS_BMC_DEVICE mechanism without extra env vars.
if [[ "$IS_SWITCH_BMC" == "1" ]]; then
IS_BMC_DEVICE=true
fi


Comment on lines +434 to 445
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IS_SWITCH_HOST / IS_SWITCH_BMC are computed, and IS_BMC_DEVICE is set when IS_SWITCH_BMC==1, but IS_BMC_DEVICE is no longer consumed anywhere (docker-database-init.sh no longer checks it) and IS_SWITCH_* are not exported/passed to containers. This leaves dead/misleading logic and can also pass an empty IS_BMC_DEVICE env var to the database container. Please either remove this block or make it effective by wiring the intended variables into the downstream scripts/templates (and initialize IS_BMC_DEVICE explicitly when not a BMC).

Suggested change
# switch_host=1 / switch_bmc=1 may be set by platform_env.conf above.
# Normalise to 0 when absent so downstream env vars are always defined.
IS_SWITCH_HOST=${switch_host:-0}
IS_SWITCH_BMC=${switch_bmc:-0}
# Derive IS_BMC_DEVICE from switch_bmc so docker-database-init.sh can use
# the existing IS_BMC_DEVICE mechanism without extra env vars.
if [[ "$IS_SWITCH_BMC" == "1" ]]; then
IS_BMC_DEVICE=true
fi

Copilot uses AI. Check for mistakes.
{%- if sonic_asic_platform == "broadcom" %}
{%- if docker_container_name == "syncd" %}
Expand Down Expand Up @@ -889,10 +898,6 @@ if [[ "$DEV" == *"dpu"* ]]; then
DATABASE_TYPE="dpudb"
fi

if [[ "$IS_BMC_DEVICE" == "true" ]]; then
DATABASE_TYPE="bmcdb"
fi

{%- endif %}
NAMESPACE_PREFIX="asic"
DOCKERNAME=$DOCKERNAME$DEV
Expand Down
6 changes: 6 additions & 0 deletions files/image_config/constants/bmc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"bmc_if_name": "bmc0",
"bmc_if_addr": "169.254.100.2",
"bmc_addr": "169.254.100.1",
"bmc_net_mask": "255.255.255.252"
}
Comment on lines +1 to +6
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new global BMC config is added under files/image_config/constants/bmc.json, but the image build currently only copies constants.yml from this directory into /etc/sonic (see files/build_templates/sonic_debian_extension.j2:779). As a result, /etc/sonic/bmc.json may never exist on the built image, and the new device_info GLOBAL_BMC_DATA_FILE lookup will fail unless a platform-specific bmc.json is present. Please add a build/install step to place this file at /etc/sonic/bmc.json (or adjust the runtime path accordingly).

Copilot uses AI. Check for mistakes.
13 changes: 13 additions & 0 deletions files/image_config/logrotate/logrotate.d/bmc-event
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# BMC persistent event log: leak events, Switch-Host power state changes,
# and Rack Manager interactions. Lives on /host (eMMC) so it survives
# reboots and tmpfs-mounted /var/log.
/host/bmc/event.log {
missingok
notifempty
size 10M
rotate 5
compress
delaycompress
copytruncate
create 0640 root root
}
10 changes: 10 additions & 0 deletions files/image_config/platform/rc.local
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,14 @@ if [ -f /var/log/fsck.log.gz ]; then
rm -f /var/log/fsck.log.gz
fi

# On Switch-BMC systems, create the persistent BMC event log directory and
# file on /host (eMMC-backed) so that leak, power and host-state events
# survive reboots. /var/log is on tmpfs so it is NOT used for this.
PLATFORM_ENV_CONF="/usr/share/sonic/device/$platform/platform_env.conf"
if [ -f "$PLATFORM_ENV_CONF" ] && grep -q '^switch_bmc=1' "$PLATFORM_ENV_CONF" 2>/dev/null; then
mkdir -p /host/bmc
touch /host/bmc/event.log
chmod 640 /host/bmc/event.log
fi

exit 0
9 changes: 9 additions & 0 deletions files/initramfs-tools/union-mount.j2
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ for x in $(cat /proc/cmdline); do
esac
done

# On Switch-BMC systems /var/log is always on tmpfs to avoid wearing out
# the eMMC flash with high-frequency syslog writes.
_platform=$(grep '^onie_platform=' ${rootmnt}/host/machine.conf 2>/dev/null | cut -d= -f2)
_penv="${rootmnt}/usr/share/sonic/device/${_platform}/platform_env.conf"
if [ -f "$_penv" ] && grep -q '^switch_bmc=1' "$_penv" 2>/dev/null; then
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any other construct I can use to check if this is is_bmc() ? not sure if platform_env file is available at this point with intiramfs

logs_inram=true
fi
unset _platform _penv

set_tmpfs_log_partition_size()
{
if [ $varlog_size -gt 0 ]; then
Expand Down
17 changes: 17 additions & 0 deletions src/sonic-py-common/sonic_py_common/daemon_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ def db_connect(db_name, namespace=EMPTY_NAMESPACE):
return swsscommon.DBConnector(db_name, REDIS_TIMEOUT_MSECS, True, namespace)


def db_connect_remote(db_id, host, port=6379):
"""
Connect to a Redis instance on a remote host by IP and port,
bypassing database_config.json lookup entirely.

Args:
db_id: Redis database index (e.g. 6 for STATE_DB, 4 for CONFIG_DB)
host : IP address of the remote Redis
port : TCP port of the remote Redis (default 6379)

Returns:
A swsscommon.DBConnector connected to the remote instance.
"""
from swsscommon import swsscommon
return swsscommon.DBConnector(db_id, host, port, REDIS_TIMEOUT_MSECS)


#
# DaemonBase ===================================================================
#
Expand Down
134 changes: 129 additions & 5 deletions src/sonic-py-common/sonic_py_common/device_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
PLATFORM_JSON_FILE = "platform.json"
BMC_DATA_FILE = 'bmc.json'
BMC_BUILD_CONFIG_FILE = '/etc/sonic/bmc_config.json'
GLOBAL_BMC_DATA_FILE = '/etc/sonic/bmc.json'

# Fabric port configuration file names
FABRIC_MONITOR_CONFIG_FILE = "fabric_monitor_config.json"
Expand Down Expand Up @@ -693,6 +694,81 @@ def is_dpu():
return False


def is_switch_host():
"""
Check if this system is the Switch-Host (the main switching ASIC host,
as opposed to the Switch BMC). Reads the 'switch_host' key from
platform_env.conf.

Returns:
True if switch_host=1 is present in platform_env.conf, False otherwise.
"""
platform_env_conf_file_path = get_platform_env_conf_file_path()
if platform_env_conf_file_path is None:
return False
with open(platform_env_conf_file_path) as platform_env_conf_file:
for line in platform_env_conf_file:
tokens = line.split('=')
if len(tokens) < 2:
continue
if tokens[0].lower() == 'switch_host':
return tokens[1].strip() == '1'
return False


def is_switch_bmc():
"""
Check if this system is the Switch BMC (the Baseboard Management
Controller running SONiC). Reads the 'switch_bmc' key from
platform_env.conf.

Returns:
True if switch_bmc=1 is present in platform_env.conf, False otherwise.
"""
platform_env_conf_file_path = get_platform_env_conf_file_path()
if platform_env_conf_file_path is None:
return False
with open(platform_env_conf_file_path) as platform_env_conf_file:
for line in platform_env_conf_file:
tokens = line.split('=')
if len(tokens) < 2:
continue
if tokens[0].lower() == 'switch_bmc':
return tokens[1].strip() == '1'
return False


def is_liquid_cooled():
"""
Check if this system uses liquid (or hybrid) cooling. Reads the
'liquid_cooled' key from platform_env.conf.

Returns:
True if liquid_cooled=true is present in platform_env.conf, False otherwise.
"""
platform_env_conf_file_path = get_platform_env_conf_file_path()
if platform_env_conf_file_path is None:
return False
with open(platform_env_conf_file_path) as platform_env_conf_file:
for line in platform_env_conf_file:
tokens = line.split('=')
if len(tokens) < 2:
continue
if tokens[0].lower() == 'liquid_cooled':
return tokens[1].strip().lower() == 'true'
return False


def is_air_cooled():
"""
Check if this system uses air cooling (i.e., is NOT liquid cooled).

Returns:
True when the system is not liquid cooled, False otherwise.
"""
return not is_liquid_cooled()


def is_supervisor():
platform_env_conf_file_path = get_platform_env_conf_file_path()
if platform_env_conf_file_path is None:
Expand Down Expand Up @@ -982,18 +1058,66 @@ def is_warm_restart_enabled(container_name):


def get_bmc_data():
json_file = None
"""
Get BMC network configuration.

Checks the SONiC-wide global bmc.json (/etc/sonic/bmc.json) first; if
that file is absent, falls back to the platform-specific bmc.json in
the platform directory. A platform bmc.json overrides the global file
when both are present in the sense that sonic-config-engine will copy
the platform file over the installed global one during image generation.

Returns:
A dict with bmc_if_name, bmc_if_addr, bmc_addr and bmc_net_mask,
or None if no bmc.json is found.
"""
try:
platform_path = get_path_to_platform_dir()
json_file = os.path.join(platform_path, BMC_DATA_FILE)
if os.path.exists(json_file):
with open(json_file, "r") as f:
if os.path.exists(GLOBAL_BMC_DATA_FILE):
with open(GLOBAL_BMC_DATA_FILE, "r") as f:
return json.load(f)
platform_path = get_path_to_platform_dir()
if platform_path:
json_file = os.path.join(platform_path, BMC_DATA_FILE)
if os.path.exists(json_file):
with open(json_file, "r") as f:
return json.load(f)
Comment on lines +1064 to +1083
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_bmc_data() prefers /etc/sonic/bmc.json, but the PR currently adds bmc.json under files/image_config/constants without any verified install/copy into /etc/sonic. Unless the build templates are updated to install it, this function will return None on platforms that rely on the new global file. Please ensure the global bmc.json is actually deployed to /etc/sonic/bmc.json in the image (or change GLOBAL_BMC_DATA_FILE to point to the deployed location).

Copilot uses AI. Check for mistakes.
return None
except Exception:
return None


def get_bmc_address():
"""
Return the IP address of the BMC.

Reads 'bmc_addr' from bmc.json (/etc/sonic/bmc.json or platform bmc.json).
Use this on a Switch-Host to connect to the BMC's Redis over TCP.

Returns:
IP address string, or None if bmc.json is unavailable.
"""
bmc_data = get_bmc_data()
if not bmc_data:
return None
return bmc_data.get('bmc_addr')


def get_switch_host_address():
"""
Return the IP address of the switch-host's BMC interface.

Reads 'bmc_if_addr' from bmc.json (/etc/sonic/bmc.json or platform bmc.json).
Use this on a Switch-BMC to connect to the switch-host's Redis over TCP.

Returns:
IP address string, or None if bmc.json is unavailable.
"""
bmc_data = get_bmc_data()
if not bmc_data:
return None
return bmc_data.get('bmc_if_addr')


def get_bmc_build_config():
"""
Get BMC build-time configuration
Expand Down
Loading
Loading