diff --git a/device/nexthop/arm64-nexthop_b27-r0/pmon_daemon_control.json b/device/nexthop/arm64-nexthop_b27-r0/pmon_daemon_control.json index de0341b4f0a..9acad76599e 100644 --- a/device/nexthop/arm64-nexthop_b27-r0/pmon_daemon_control.json +++ b/device/nexthop/arm64-nexthop_b27-r0/pmon_daemon_control.json @@ -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, diff --git a/dockers/docker-database/database_config.json.j2 b/dockers/docker-database/database_config.json.j2 index 0e8c6bc93b4..b97589d3733 100644 --- a/dockers/docker-database/database_config.json.j2 +++ b/dockers/docker-database/database_config.json.j2 @@ -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":{ @@ -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" : { diff --git a/dockers/docker-database/docker-database-init.sh b/dockers/docker-database/docker-database-init.sh index dc6247bd767..961ddddd9fc 100755 --- a/dockers/docker-database/docker-database-init.sh +++ b/dockers/docker-database/docker-database-init.sh @@ -41,10 +41,6 @@ then fi fi -if [[ $IS_BMC_DEVICE == "true" ]] -then - export DATABASE_TYPE="bmcdb" -fi export BMP_DB_PORT=6400 diff --git a/dockers/docker-database/multi_database_config.json.j2 b/dockers/docker-database/multi_database_config.json.j2 index 8d633e685b7..54d7cc33767 100644 --- a/dockers/docker-database/multi_database_config.json.j2 +++ b/dockers/docker-database/multi_database_config.json.j2 @@ -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":{ @@ -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" : { diff --git a/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 b/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 index a3b8cd192bb..1352b209911 100644 --- a/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 +++ b/dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2 @@ -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 @@ -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 diff --git a/dockers/docker-platform-monitor/docker_init.j2 b/dockers/docker-platform-monitor/docker_init.j2 index 11c834f782e..b4cfad016f7 100755 --- a/dockers/docker-platform-monitor/docker_init.j2 +++ b/dockers/docker-platform-monitor/docker_init.j2 @@ -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 diff --git a/files/build_templates/docker_image_ctl.j2 b/files/build_templates/docker_image_ctl.j2 index f874cc21499..cbc67b674b3 100644 --- a/files/build_templates/docker_image_ctl.j2 +++ b/files/build_templates/docker_image_ctl.j2 @@ -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" %} @@ -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 + {%- if sonic_asic_platform == "broadcom" %} {%- if docker_container_name == "syncd" %} @@ -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 diff --git a/files/image_config/constants/bmc.json b/files/image_config/constants/bmc.json new file mode 100644 index 00000000000..c1fad5f27e9 --- /dev/null +++ b/files/image_config/constants/bmc.json @@ -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" +} diff --git a/files/image_config/logrotate/logrotate.d/bmc-event b/files/image_config/logrotate/logrotate.d/bmc-event new file mode 100644 index 00000000000..c682194cdb0 --- /dev/null +++ b/files/image_config/logrotate/logrotate.d/bmc-event @@ -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 +} diff --git a/files/image_config/platform/rc.local b/files/image_config/platform/rc.local index 898f581f01a..bbf203af6ab 100755 --- a/files/image_config/platform/rc.local +++ b/files/image_config/platform/rc.local @@ -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 diff --git a/files/initramfs-tools/union-mount.j2 b/files/initramfs-tools/union-mount.j2 index 6df78736405..88ae34889eb 100644 --- a/files/initramfs-tools/union-mount.j2 +++ b/files/initramfs-tools/union-mount.j2 @@ -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 + logs_inram=true +fi +unset _platform _penv + set_tmpfs_log_partition_size() { if [ $varlog_size -gt 0 ]; then diff --git a/src/sonic-py-common/sonic_py_common/daemon_base.py b/src/sonic-py-common/sonic_py_common/daemon_base.py index fd98a12b28e..c2ada9b1229 100644 --- a/src/sonic-py-common/sonic_py_common/daemon_base.py +++ b/src/sonic-py-common/sonic_py_common/daemon_base.py @@ -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 =================================================================== # diff --git a/src/sonic-py-common/sonic_py_common/device_info.py b/src/sonic-py-common/sonic_py_common/device_info.py index fc13ba65e0d..657eb6356ac 100644 --- a/src/sonic-py-common/sonic_py_common/device_info.py +++ b/src/sonic-py-common/sonic_py_common/device_info.py @@ -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" @@ -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: @@ -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) 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 diff --git a/src/sonic-py-common/tests/device_info_test.py b/src/sonic-py-common/tests/device_info_test.py index 85806a1ce00..802e207bcd8 100644 --- a/src/sonic-py-common/tests/device_info_test.py +++ b/src/sonic-py-common/tests/device_info_test.py @@ -367,6 +367,146 @@ def test_get_dpu_list(self, mock_get_platform, mock_is_dpu, mock_get_platform_js mock_get_platform_json_data.return_value = {"DPUS": {"dpu0": {}, "dpu1": {}}} assert device_info.get_dpu_list() == ["dpu0", "dpu1"] + # ------------------------------------------------------------------ + # Tests for BMC platform detection APIs + # ------------------------------------------------------------------ + + PLATFORM_ENV_BMC_CONTENTS = """\ +switch_bmc=1 +liquid_cooled=true +""" + + PLATFORM_ENV_HOST_CONTENTS = """\ +switch_host=1 +liquid_cooled=true +""" + + PLATFORM_ENV_AIR_CONTENTS = """\ +switch_host=1 +""" + + @mock.patch("sonic_py_common.device_info.get_platform_env_conf_file_path") + def test_is_switch_bmc(self, mock_get_env_path): + # platform_env.conf not found + mock_get_env_path.return_value = None + assert device_info.is_switch_bmc() is False + + open_bmc = mock.mock_open(read_data=self.PLATFORM_ENV_BMC_CONTENTS) + with mock.patch("{}.open".format(BUILTINS), open_bmc): + mock_get_env_path.return_value = "/usr/share/sonic/platform/platform_env.conf" + assert device_info.is_switch_bmc() is True + + open_host = mock.mock_open(read_data=self.PLATFORM_ENV_HOST_CONTENTS) + with mock.patch("{}.open".format(BUILTINS), open_host): + assert device_info.is_switch_bmc() is False + + @mock.patch("sonic_py_common.device_info.get_platform_env_conf_file_path") + def test_is_switch_host(self, mock_get_env_path): + # platform_env.conf not found + mock_get_env_path.return_value = None + assert device_info.is_switch_host() is False + + open_host = mock.mock_open(read_data=self.PLATFORM_ENV_HOST_CONTENTS) + with mock.patch("{}.open".format(BUILTINS), open_host): + mock_get_env_path.return_value = "/usr/share/sonic/platform/platform_env.conf" + assert device_info.is_switch_host() is True + + open_bmc = mock.mock_open(read_data=self.PLATFORM_ENV_BMC_CONTENTS) + with mock.patch("{}.open".format(BUILTINS), open_bmc): + assert device_info.is_switch_host() is False + + @mock.patch("sonic_py_common.device_info.get_platform_env_conf_file_path") + def test_is_liquid_cooled(self, mock_get_env_path): + # platform_env.conf not found + mock_get_env_path.return_value = None + assert device_info.is_liquid_cooled() is False + + open_lc = mock.mock_open(read_data=self.PLATFORM_ENV_HOST_CONTENTS) + with mock.patch("{}.open".format(BUILTINS), open_lc): + mock_get_env_path.return_value = "/usr/share/sonic/platform/platform_env.conf" + assert device_info.is_liquid_cooled() is True + + open_air = mock.mock_open(read_data=self.PLATFORM_ENV_AIR_CONTENTS) + with mock.patch("{}.open".format(BUILTINS), open_air): + assert device_info.is_liquid_cooled() is False + + @mock.patch("sonic_py_common.device_info.is_liquid_cooled") + def test_is_air_cooled(self, mock_lc): + mock_lc.return_value = True + assert device_info.is_air_cooled() is False + + mock_lc.return_value = False + assert device_info.is_air_cooled() is True + + @mock.patch("os.path.exists") + @mock.patch("sonic_py_common.device_info.get_path_to_platform_dir") + def test_get_bmc_data(self, mock_platform_dir, mock_exists): + BMC_GLOBAL = '{"bmc_if_name": "bmc0", "bmc_if_addr": "169.254.100.2", ' \ + '"bmc_addr": "169.254.100.1", "bmc_net_mask": "255.255.255.252"}' + BMC_PLATFORM = '{"bmc_if_name": "usb0", "bmc_if_addr": "169.254.0.2", ' \ + '"bmc_addr": "169.254.0.1", "bmc_net_mask": "255.255.255.252"}' + + # global bmc.json exists – should be returned + def exists_global(path): + return path == device_info.GLOBAL_BMC_DATA_FILE + + mock_exists.side_effect = exists_global + with mock.patch("{}.open".format(BUILTINS), + mock.mock_open(read_data=BMC_GLOBAL)): + result = device_info.get_bmc_data() + assert result is not None + assert result["bmc_addr"] == "169.254.100.1" + assert result["bmc_if_name"] == "bmc0" + + # global bmc.json absent, platform bmc.json present + mock_platform_dir.return_value = "/usr/share/sonic/platform" + + def exists_platform(path): + return path == "/usr/share/sonic/platform/bmc.json" + + mock_exists.side_effect = exists_platform + with mock.patch("{}.open".format(BUILTINS), + mock.mock_open(read_data=BMC_PLATFORM)): + result = device_info.get_bmc_data() + assert result is not None + assert result["bmc_addr"] == "169.254.0.1" + assert result["bmc_if_name"] == "usb0" + + # Neither file present + mock_exists.side_effect = lambda p: False + result = device_info.get_bmc_data() + assert result is None + + @mock.patch("sonic_py_common.device_info.get_bmc_data") + def test_get_bmc_address(self, mock_bmc_data): + BMC_DATA = { + "bmc_if_name": "bmc0", + "bmc_if_addr": "169.254.100.2", + "bmc_addr": "169.254.100.1", + "bmc_net_mask": "255.255.255.252" + } + + mock_bmc_data.return_value = None + assert device_info.get_bmc_address() is None + + mock_bmc_data.return_value = BMC_DATA + assert device_info.get_bmc_address() == "169.254.100.1" + + @mock.patch("sonic_py_common.device_info.get_bmc_data") + def test_get_switch_host_address(self, mock_bmc_data): + BMC_DATA = { + "bmc_if_name": "bmc0", + "bmc_if_addr": "169.254.100.2", + "bmc_addr": "169.254.100.1", + "bmc_net_mask": "255.255.255.252" + } + + mock_bmc_data.return_value = None + assert device_info.get_switch_host_address() is None + + mock_bmc_data.return_value = BMC_DATA + assert device_info.get_switch_host_address() == "169.254.100.2" + @classmethod def teardown_class(cls): print("TEARDOWN") diff --git a/src/sonic-py-common/tests/test_daemon_base.py b/src/sonic-py-common/tests/test_daemon_base.py new file mode 100644 index 00000000000..b5aeab0df36 --- /dev/null +++ b/src/sonic-py-common/tests/test_daemon_base.py @@ -0,0 +1,39 @@ +import sys + +if sys.version_info.major == 3: + from unittest import mock +else: + import mock + +import pytest + +from sonic_py_common import daemon_base + + +class TestDaemonBase: + + def _make_swss_mock(self): + mock_swsscommon_inner = mock.MagicMock() + mock_swsscommon_outer = mock.MagicMock() + mock_swsscommon_outer.swsscommon = mock_swsscommon_inner + return mock_swsscommon_outer, mock_swsscommon_inner + + def test_db_connect_remote(self): + """db_connect_remote uses DBConnector(db_id, host, port, timeout).""" + outer, inner = self._make_swss_mock() + with mock.patch.dict("sys.modules", + {"swsscommon": outer, + "swsscommon.swsscommon": inner}): + daemon_base.db_connect_remote(6, "redis_bmc", 6379) + inner.DBConnector.assert_called_once_with( + 6, "redis_bmc", 6379, daemon_base.REDIS_TIMEOUT_MSECS) + + def test_db_connect_remote_default_port(self): + """db_connect_remote defaults to port 6379.""" + outer, inner = self._make_swss_mock() + with mock.patch.dict("sys.modules", + {"swsscommon": outer, + "swsscommon.swsscommon": inner}): + daemon_base.db_connect_remote(4, "redis_switch_host") + inner.DBConnector.assert_called_once_with( + 4, "redis_switch_host", 6379, daemon_base.REDIS_TIMEOUT_MSECS)