Skip to content

Commit e4ed588

Browse files
committed
Add support for liquid cooling of mellnox api
Signed-off-by: Yuanzhe, Liu <[email protected]>
1 parent bb0612a commit e4ed588

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

dockers/docker-platform-monitor/docker-pmon.supervisord.conf.j2

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,14 @@ dependent_startup_wait_for=rsyslogd:running {% if delay_non_critical_daemon %}de
218218
{% if thermalctld.thermal_monitor_update_elapsed_threshold is defined and thermalctld.thermal_monitor_update_elapsed_threshold is not none %}
219219
{%- set options = options + " --thermal-monitor-update-elapsed-threshold " + thermalctld.thermal_monitor_update_elapsed_threshold|string %}
220220
{% endif -%}
221+
222+
{% if thermalctld.enable_liquid_cooling %}
223+
{%- set options = options + " --enable_liquid_cooling" %}
224+
{% endif -%}
225+
226+
{% if thermalctld.liquid_cooling_update_interval %}
227+
{%- set options = options ~ " --liquid_cooling_update_interval " ~ thermalctld.liquid_cooling_update_interval %}
228+
{% endif -%}
221229
{% endif -%}
222230

223231
{%- set command = base_command ~ options %}

platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,13 @@ def __init__(self):
121121
self._RJ45_port_inited = False
122122
self._RJ45_port_list = None
123123

124+
124125
# Build the CPO port list from platform.json and hwsku.json
125126
self._cpo_port_inited = False
126127
self._cpo_port_list = None
127128

129+
self.liquid_cooling = None
130+
128131
Chassis.chassis_instance = self
129132

130133
self.module_host_mgmt_initializer = module_host_mgmt_initializer.ModuleHostMgmtInitializer()
@@ -1118,6 +1121,20 @@ def is_replaceable(self):
11181121
"""
11191122
return False
11201123

1124+
1125+
##############################################
1126+
# LiquidCooling methods
1127+
##############################################
1128+
1129+
def initialize_liquid_cooling(self):
1130+
if not self.liquid_cooling:
1131+
from .liquid_cooling import LiquidCooling
1132+
self.liquid_cooling = LiquidCooling()
1133+
1134+
def get_liquid_cooling(self):
1135+
self.initialize_liquid_cooling()
1136+
return self.liquid_cooling
1137+
11211138

11221139
class ModularChassis(Chassis):
11231140
def __init__(self):
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#
2+
# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3+
# Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
#############################################################################
19+
# Mellanox
20+
#
21+
# Module contains an implementation of SONiC Platform Base API and
22+
# provides the liquid cooling status which are available in the platform
23+
#
24+
#############################################################################
25+
26+
try:
27+
from sonic_platform_base.liquid_cooling_base import LeakageSensorBase,LiquidCoolingBase
28+
from sonic_py_common.logger import Logger
29+
from . import utils
30+
import os
31+
import glob
32+
import logging
33+
except ImportError as e:
34+
raise ImportError(str(e) + "- required module not found")
35+
36+
logging.basicConfig(level=logging.DEBUG)
37+
logger = logging.getLogger(__name__)
38+
39+
LIQUID_COOLING_SENSOR_PATH = "/var/run/hw-management/system/"
40+
41+
class LeakageSensor(LeakageSensorBase):
42+
def __init__(self, name,path):
43+
super(LeakageSensor, self).__init__(name)
44+
self.path = path
45+
46+
def is_leak(self):
47+
content = utils.read_int_from_file(self.path, 'N/A')
48+
49+
if content == 1:
50+
self.leaking = False
51+
return False
52+
elif content == 0:
53+
self.leaking = True
54+
return True
55+
else:
56+
logger.error(f"Failed to read leakage sensor {self.name} value: {content}")
57+
return 'N/A'
58+
59+
class LiquidCooling(LiquidCoolingBase):
60+
"""Platform-specific Liquid Cooling class"""
61+
62+
def __init__(self):
63+
64+
self.leakage_sensors_num = utils.read_int_from_file("/var/run/hw-management/config/leakage_counter")
65+
self.leakage_sensors = []
66+
67+
sensor_files = glob.glob(os.path.join(LIQUID_COOLING_SENSOR_PATH, "leakage*"))
68+
sensor_files.sort(key=lambda x: int(x.split("leakage")[-1]))
69+
70+
for sensor_path in sensor_files[:self.leakage_sensors_num]:
71+
sensor_name = os.path.basename(sensor_path)
72+
self.leakage_sensors.append(LeakageSensor(sensor_name, sensor_path))
73+
74+
super(LiquidCooling, self).__init__(self.leakage_sensors_num, self.leakage_sensors)
75+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import os
2+
import sys
3+
from unittest import mock
4+
from sonic_platform.liquid_cooling import LiquidCooling, LeakageSensor
5+
from sonic_platform.utils import read_int_from_file
6+
7+
def test_leakage_sensor_init():
8+
sensor = LeakageSensor("leakage1", "/test/path")
9+
assert sensor.name == "leakage1"
10+
assert sensor.path == "/test/path"
11+
12+
def test_leakage_sensor_is_leak():
13+
sensor = LeakageSensor("leakage1", "/test/path")
14+
15+
# Test when file exists and content is "1" (no leak)
16+
with mock.patch('os.path.exists') as mock_exists:
17+
with mock.patch('builtins.open', mock.mock_open(read_data="1")):
18+
mock_exists.return_value = True
19+
assert sensor.is_leak() is False
20+
21+
# Test when file exists and content is "0" (leak detected)
22+
with mock.patch('os.path.exists') as mock_exists:
23+
with mock.patch('builtins.open', mock.mock_open(read_data="0")):
24+
mock_exists.return_value = True
25+
assert sensor.is_leak() is True
26+
27+
# Test when file does not exist
28+
with mock.patch('os.path.exists') as mock_exists:
29+
mock_exists.return_value = False
30+
assert sensor.is_leak() == 'N/A'
31+
32+
def test_liquid_cooling_init():
33+
with mock.patch('os.path.exists') as mock_exists, \
34+
mock.patch('os.path.join', side_effect=lambda *args: "/".join(args)) as mock_join, \
35+
mock.patch('glob.glob') as mock_glob, \
36+
mock.patch('sonic_platform.utils.read_int_from_file', return_value=4) as mock_read_int:
37+
38+
# Setup mock to simulate 4 leakage sensors
39+
mock_exists.side_effect = [True, True, True, True]
40+
mock_glob.return_value = [
41+
"/var/run/hw-management/system/leakage1",
42+
"/var/run/hw-management/system/leakage2",
43+
"/var/run/hw-management/system/leakage3",
44+
"/var/run/hw-management/system/leakage4"
45+
]
46+
47+
liquid_cooling = LiquidCooling()
48+
49+
# Verify the number of sensors initialized
50+
assert liquid_cooling.get_num_leak_sensors() == 4
51+
52+
53+
with mock.patch.object(liquid_cooling.leakage_sensors[3], 'is_leak', return_value=False) as mock_sensor1:
54+
sensors = liquid_cooling.get_leak_sensor_status()
55+
assert len(sensors) == 3

0 commit comments

Comments
 (0)