sonic-buildimage/platform/mellanox/mlnx-platform-api/tests/test_modules_mgmt.py
dbarashinvd ac35a0fafb add unit tests for CMIS host management feature (#18211)
* add unit tests for CMIS host management feature
2024-03-21 07:00:43 +08:00

801 lines
43 KiB
Python

#
# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
# Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
import queue
import sys
import threading
import time
import types
import unittest
from mock import MagicMock, patch, mock_open, Mock
if sys.version_info.major == 3:
from unittest import mock
else:
import mock
test_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(test_path)
sys.path.insert(0, modules_path)
from sonic_platform.device_data import DeviceDataManager
from sonic_py_common import device_info
from sonic_platform import modules_mgmt
from sonic_platform.modules_mgmt import ModulesMgmtTask
from sonic_platform_base.sonic_xcvr.api.public.cmis import CmisApi
from sonic_platform_base.sonic_xcvr.xcvr_eeprom import XcvrEeprom
from sonic_platform_base.sonic_xcvr.codes.public.cmis import CmisCodes
from sonic_platform_base.sonic_xcvr.mem_maps.public.cmis import CmisMemMap
from sonic_platform_base.sonic_xcvr.fields import consts
DEFAULT_NUM_OF_PORTS_1 = 1
DEFAULT_NUM_OF_PORTS_3 = 3
DEFAULT_NUM_OF_PORTS_32 = 32
POLLER_EXECUTED = False
def _mock_sysfs_default_file_content():
return {
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("0"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("1"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("2"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("0"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("1"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("2"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("0"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("1"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("2"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("0"): "48",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("1"): "48",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("2"): "48",
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("0"): "0",
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("1"): "0",
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("2"): "0",
modules_mgmt.SYSFS_INDEPENDENT_FD_HW_RESET: "",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT: "48",
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("0"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("1"): "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("2"): "1",
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE: "1",
modules_mgmt.PROC_CMDLINE: ""
}
mock_file_content = _mock_sysfs_default_file_content()
class MockPoller:
def __init__(self, modules_mgmt_task_stopping_event, modules_mgmt_thrd=None, num_of_ports=3, port_plug_out=False
, feature_enabled=True, warm_reboot=False, port_plug_in=False, sleep_timeout=False):
self.fds_dict = {}
self.poller_iteration_count = 0
self.modules_mgmt_task_stopping_event = modules_mgmt_task_stopping_event
self.modules_mgmt_thrd = modules_mgmt_thrd
self.num_of_ports = num_of_ports
self.port_plug_out = port_plug_out
self.port_plug_in = port_plug_in
self.feature_enabled = feature_enabled
self.warm_reboot = warm_reboot
self.port_plug_out_changed = False
self.port_plug_in_changed = False
self.sleep_timeout = sleep_timeout
def register(self, fd, attrs):
self.fds_dict[fd.fileno()] = { fd : attrs }
assert fd.fileno() in self.fds_dict
def unregister(self, fd):
if fd.fileno() in self.fds_dict.keys():
del self.fds_dict[fd.fileno()]
assert fd.fileno() not in self.fds_dict.keys()
def poll(self, timeout=1000):
global POLLER_EXECUTED
assert len(self.modules_mgmt_thrd.sfp_port_dict_initial) == self.num_of_ports
assert self.modules_mgmt_thrd.is_supported_indep_mods_system == self.feature_enabled
# counting the number of poller iterations to know when to do the checks after plug out (and plug in)
# have to check at least on iteration 7 to let ports reach final state
self.poller_iteration_count += 1
if self.num_of_ports > 0:
if not self.port_plug_out_changed:
if self.port_plug_out:
# return first fd registered with some made up event number 870
fd_no_to_return = list(self.fds_dict.keys())[0]
fd = list(self.fds_dict[fd_no_to_return].keys())[0]
fd.set_file_int_content(0)
event_to_return = 870
self.port_plug_out_changed = True
return [(fd_no_to_return, event_to_return)]
if not self.port_plug_in_changed:
if self.port_plug_in:
# return first fd registered with some made up event number 871
fd_no_to_return = list(self.fds_dict.keys())[0]
fd = list(self.fds_dict[fd_no_to_return].keys())[0]
fd.set_file_int_content(1)
event_to_return = 871
self.port_plug_in_changed = True
return [(fd_no_to_return, event_to_return)]
if 7 == self.poller_iteration_count:
# when feature is enabled, need to check for each port both power_good and hw_present sysfs for
# cmis non-flat memory cables
num_of_sysfs_to_check = self.num_of_ports if (not self.port_plug_out or not self.feature_enabled
or self.warm_reboot) else self.num_of_ports * 2
for i in range(num_of_sysfs_to_check):
# when feature is enabled, power_good sysfs is also registered for cmis non-flat memory cables
# so each SW controlled port has 2 fds registered
port_to_test = i if not self.feature_enabled else int(i / 2)
assert self.modules_mgmt_thrd.sfp_port_dict_initial[port_to_test].port_num == port_to_test
assert self.modules_mgmt_thrd.sfp_port_dict_initial[
port_to_test].initial_state == modules_mgmt.STATE_HW_NOT_PRESENT
if self.feature_enabled:
module_obj = self.modules_mgmt_thrd.fds_mapping_to_obj[list(self.fds_dict.keys())[i]][
'module_obj']
assert module_obj.port_num == port_to_test
if not self.warm_reboot:
# in tests other than warm reboot it creates only SW control ports
if not self.port_plug_out:
assert module_obj.final_state == modules_mgmt.STATE_SW_CONTROL
else:
assert module_obj.final_state == modules_mgmt.STATE_HW_NOT_PRESENT
else:
if not self.port_plug_out:
assert module_obj.final_state == modules_mgmt.STATE_HW_PRESENT
# in warm reboot test with plug out plug in test creates only FW control ports
elif self.port_plug_out and self.port_plug_in:
assert module_obj.final_state == modules_mgmt.STATE_FW_CONTROL
else:
assert module_obj.final_state == modules_mgmt.STATE_HW_NOT_PRESENT
POLLER_EXECUTED = True
self.modules_mgmt_task_stopping_event.set()
if self.sleep_timeout:
time.sleep(timeout/1000)
return []
class MockOpen:
def __init__(self, name='', file_no=None, indep_mode_supported=True):
self.name = name
self.file_no = file_no
self.indep_mode_supported = indep_mode_supported
self.retint = None
self.curr = 0
def read(self):
if self.fileno() in [SAI_PROFILE_FD_FILENO]:
pass
else:
# if return value was changed, i.e. sysfs content changed from 1 to 0 to simulate plug out
if self.retint is not None:
return str(self.retint)
# return default values (can be changed per test)
else:
return mock_file_content[self.name]
def readline(self):
# if trying to read sai profile file, according to fd fileno
if self.fileno() in [SAI_PROFILE_FD_FILENO]:
if self.indep_mode_supported:
return "SAI_INDEPENDENT_MODULE_MODE=1"
else:
return ""
else:
return mock_file_content[self.name]
def fileno(self):
return self.file_no
def seek(self, seek_val):
self.curr = seek_val
def close(self):
pass
def write(self, write_val):
self.set_file_int_content(write_val)
def set_file_int_content(self, retint):
self.retint = str(retint)
mock_file_content[self.name] = str(retint)
def __enter__(self):
return self
def __exit__(self, filename, *args, **kwargs):
pass
class MockPollerStopEvent:
def __init__(self, modules_mgmt_task_stopping_event, modules_mgmt_thrd=None, num_of_ports=DEFAULT_NUM_OF_PORTS_3
, feature_enabled=True, ports_connected=True, fw_controlled_ports=False, sleep_timeout=False):
self.fds_dict = {}
self.modules_mgmt_task_stopping_event = modules_mgmt_task_stopping_event
self.modules_mgmt_thrd = modules_mgmt_thrd
self.num_of_ports = num_of_ports
self.feature_enabled = feature_enabled
self.ports_connected = ports_connected
self.sleep_timeout = sleep_timeout
self.fw_controlled_ports = fw_controlled_ports
def register(self, fd, attrs):
self.fds_dict[fd.fileno()] = 1 & attrs
assert fd.fileno() in self.fds_dict
def poll(self, timeout=0):
assert len(self.modules_mgmt_thrd.sfp_port_dict_initial) == self.num_of_ports
assert self.modules_mgmt_thrd.is_supported_indep_mods_system == self.feature_enabled
global POLLER_EXECUTED
if self.num_of_ports > 0:
# when feature is enabled, need to check for each port both power_good and hw_present sysfs for
# cmis non-flat memory cables
ports_to_test = self.num_of_ports if (not self.feature_enabled or not self.ports_connected
or self.fw_controlled_ports) else self.num_of_ports * 2
for i in range(ports_to_test):
# when feature is enabled, power_good sysfs is also registered for cmis non-flat memory cables
port_to_test = i if (not self.feature_enabled or not self.ports_connected
or self.fw_controlled_ports) else int(i / 2)
assert self.modules_mgmt_thrd.sfp_port_dict_initial[port_to_test].port_num == port_to_test
assert self.modules_mgmt_thrd.sfp_port_dict_initial[port_to_test].initial_state == modules_mgmt.STATE_HW_NOT_PRESENT
module_obj = self.modules_mgmt_thrd.fds_mapping_to_obj[list(self.fds_dict.keys())[i]]['module_obj']
assert module_obj.port_num == port_to_test
if self.ports_connected:
if self.feature_enabled:
if self.fw_controlled_ports:
assert module_obj.final_state == modules_mgmt.STATE_FW_CONTROL
else:
assert module_obj.final_state == modules_mgmt.STATE_SW_CONTROL
else:
assert module_obj.final_state == modules_mgmt.STATE_HW_PRESENT
else:
assert module_obj.final_state == modules_mgmt.STATE_HW_NOT_PRESENT
POLLER_EXECUTED = True
else:
POLLER_EXECUTED = True
self.modules_mgmt_task_stopping_event.set()
if self.sleep_timeout:
time.sleep(timeout/1000)
return []
def _mock_is_file_indep_mode_disabled_content():
return {
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_HW_RESET: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL: True,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0"): True,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1"): True,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2"): True,
'//usr/share/sonic/platform/ACS-MSN4700/sai.profile' : True
}
mock_is_file_indep_mode_disabled_content = _mock_is_file_indep_mode_disabled_content()
def mock_is_file_indep_mode_disabled(file_path, **kwargs):
return mock_is_file_indep_mode_disabled_content[file_path]
def _mock_is_file_indep_mode_enabled_content():
return {
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_HW_RESET: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT: True,
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL: True,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("0"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("1"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("2"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("0"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("1"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("2"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("0"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("1"): True,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("2"): True,
'//usr/share/sonic/platform/ACS-MSN4700/sai.profile' : True
}
mock_is_file_indep_mode_enabled_content = _mock_is_file_indep_mode_enabled_content()
def mock_is_file_indep_mode_enabled(file_path, **kwargs):
return mock_is_file_indep_mode_enabled_content[file_path]
def mock_read_int_from_file(filename, *args):
return_dict = {
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0") : 1,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1") : 1,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2") : 1
}
return return_dict[filename]
class MockXcvrEeprom():
def __init__(self, is_flat_memory, mem_map):
self.is_flat_memory = is_flat_memory
self.mem_map = mem_map
def is_cmis_api(self):
return self.is_cmis_api
def is_flat_memory(self):
return self.is_flat_memory
def read(self, field):
if consts.FLAT_MEM_FIELD == field:
return 0 if self.is_flat_memory else 1
else:
return 0
class MockXcvrapi:
def __init__(self, is_cmis_api=True, is_flat_memory_bool=False):
self.is_cmis_api = is_cmis_api
self.is_flat_memory_bool = is_flat_memory_bool
self.xcvr_eeprom = MagicMock(autospec=XcvrEeprom, return_value=MockXcvrEeprom(is_flat_memory_bool, CmisMemMap(CmisCodes)))
def is_flat_memory(self):
return self.is_flat_memory_bool
def xcvr_eeprom(self):
return self.xcvr_eeprom
class MockSFPxcvrapi:
def __init__(self, xcvr_api_is_cmis_api=True, xcvr_eeprom_is_flat_memory=False):
self.xcvr_api = Mock(spec=CmisApi(MockXcvrEeprom(False, CmisMemMap(CmisCodes))), return_value=MockXcvrapi(xcvr_api_is_cmis_api, xcvr_eeprom_is_flat_memory))
self.xcvr_api_is_cmis_api = xcvr_api_is_cmis_api
self.xcvr_eeprom_is_flat_memory = xcvr_eeprom_is_flat_memory
self.xcvr_api.is_flat_memory = types.MethodType(self.is_flat_memory, self)
def get_xcvr_api(self):
return self.xcvr_api
def is_flat_memory(self, ref):
return self.xcvr_eeprom_is_flat_memory
def check_power_cap(port, module_sm_obj):
pass
SAI_PROFILE_FD_FILENO = 99
class TestModulesMgmt(unittest.TestCase):
"""Test class to test modules_mgmt.py. The test cases covers:
1. cables detection for 1 to 3 ports - feature disabled / enabled / poller
2. cable disconnection - plug out
3. cable reconnection - plug in
4. warm reboot normal flow with FW ports
5. warm reboot flow with FW ports plugged out
6. warm reboot flow with FW ports plugged out and then plugged in (stays FW controlled, no SFP mock change)
7. test 32 FW controlled (non cmis flat mem) cables powered off
8. test 32 SW controlled (cmis active non flat mem) cables powered off
"""
def _mock_sysfs_file_content(self):
return {
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE : "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD : "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON : "0",
modules_mgmt.SYSFS_INDEPENDENT_FD_HW_RESET : "",
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT : "48",
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL : "1",
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0") : "1",
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1") : "1",
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2") : "1",
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("0"): "0"
}
def mock_open_builtin(self, file_name, feature_enabled=True):
return_dict = {
(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0"), 'r') : MockOpen(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0"), 100),
(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1"), 'r') : MockOpen(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1"), 101),
(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2"), 'r') : MockOpen(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2"), 102),
'//usr/share/sonic/platform/ACS-MSN4700/sai.profile' : MockOpen('//usr/share/sonic/platform/ACS-MSN4700/sai.profile'
, SAI_PROFILE_FD_FILENO, feature_enabled),
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0") : MockOpen(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0"), 100),
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1") : MockOpen(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1"), 101),
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2") : MockOpen(modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2"), 102),
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("0"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("0"), 0),
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("1"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("1"), 1),
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("2"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("2"), 2),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("0"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("0"), 200),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("1"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("1"), 201),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("2"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("2"), 202),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("0"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("0"), 300),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("1"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("1"), 301),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("2"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("2"), 302),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("0"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("0"), 500),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("1"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("1"), 501),
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("2"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("2"), 502),
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("0"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("0"), 602),
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("1"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("1"), 602),
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("2"): MockOpen(modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("2"), 602),
modules_mgmt.PROC_CMDLINE: MockOpen(modules_mgmt.PROC_CMDLINE, self.fd_number_by_fd_name_dict[modules_mgmt.PROC_CMDLINE])
}
return return_dict[file_name]
# side effects are used in mock when want to create different mocks per variable, i.e. here it's filename
# see below mock_open_new_side_effect_poller_test where returning a new MockOpen passing it the filename
def mock_open_new_side_effect_feature_disabled(self, filename, *args, **kwargs):
mock_context = MagicMock()
mock_context.__enter__.return_value = self.mock_open_builtin(filename, False)
mock_context.__exit__.return_value = False
return mock_context
def mock_open_new_side_effect_feature_enabled(self, filename, *args, **kwargs):
mock_context = MagicMock()
mock_context.__enter__.return_value = self.mock_open_builtin(filename)
mock_context.__exit__.return_value = False
return mock_context
def mock_open_new_side_effect_poller_test(self, filename, *args, **kwargs):
if filename in ['//usr/share/sonic/platform/ACS-MSN4700/sai.profile']:
mock_context = MagicMock()
mock_context.__enter__.return_value = MockOpen(filename, SAI_PROFILE_FD_FILENO)
mock_context.__exit__.return_value = False
return mock_context
else:
mock_context = MagicMock()
mock_open_new = MockOpen(filename, self.fd_number_by_fd_name_dict[filename])
mock_context.return_value = mock_open_new
mock_context.__enter__.return_value = mock_open_new
mock_context.__exit__.return_value = False
if 'hw_present' in filename or 'power_on' in filename or 'freq' in filename or 'control' in filename:
return mock_context
else:
return mock_context.return_value
def mock_open_new_side_effect_warm_reboot(self, filename, *args, **kwargs):
if filename in ['//usr/share/sonic/platform/ACS-MSN4700/sai.profile']:
mock_context = MagicMock()
mock_context.__enter__.return_value = MockOpen(filename, SAI_PROFILE_FD_FILENO)
mock_context.__exit__.return_value = False
return mock_context
else:
mock_open_new = MockOpen(filename, self.fd_number_by_fd_name_dict[filename])
return mock_open_new
def setUp(cls):
cls.modules_mgmt_task_stopping_event = threading.Event()
cls.modules_changes_queue = queue.Queue()
global POLLER_EXECUTED
POLLER_EXECUTED = False
# start modules_mgmt thread and the test in poller part
cls.modules_mgmt_thrd = ModulesMgmtTask(main_thread_stop_event=cls.modules_mgmt_task_stopping_event,
q=cls.modules_changes_queue)
cls.modules_mgmt_thrd.check_power_cap = check_power_cap
assert cls.modules_mgmt_thrd.sfp_port_dict_initial == {}
@classmethod
def setup_class(cls):
os.environ["MLNX_PLATFORM_API_UNIT_TESTING"] = "1"
cls.fd_number_by_fd_name_dict = {
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("0") : 100,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("1") : 101,
modules_mgmt.SYSFS_LEGACY_FD_PRESENCE.format("2") : 102,
'//usr/share/sonic/platform/ACS-MSN4700/sai.profile' : SAI_PROFILE_FD_FILENO,
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("0") : 0,
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("1") : 1,
modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format("2") : 2,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("0") : 200,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("1") : 201,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format("2") : 202,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("0") : 300,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("1") : 301,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format("2") : 302,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("0") : 500,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("1") : 501,
modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_LIMIT.format("2") : 502,
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("0") : 600,
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("1") : 601,
modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format("2") : 602,
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("0") : 700,
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("1") : 701,
modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("2") : 702,
modules_mgmt.PROC_CMDLINE : 800
}
# mock the directory holding relevant sai.profile
device_info.get_paths_to_platform_and_hwsku_dirs = mock.MagicMock(return_value=('', '/usr/share/sonic/platform/ACS-MSN4700'))
@patch('sonic_platform.device_data.DeviceDataManager.get_sfp_count', MagicMock(return_value=DEFAULT_NUM_OF_PORTS_3))
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_disabled))
@patch('sonic_platform.utils.read_int_from_file', MagicMock(side_effect=mock_read_int_from_file))
@patch('builtins.open', spec=open)
def test_mdf_all_ports_feature_disabled(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_feature_disabled
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_3
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, feature_enabled=False))):
self.modules_mgmt_thrd.run()
@patch('sonic_platform.device_data.DeviceDataManager.get_sfp_count', MagicMock(return_value=DEFAULT_NUM_OF_PORTS_3))
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi()))
def test_mdf_all_ports_feature_enabled(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_feature_enabled
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_3
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi()))
def test_modules_mgmt_poller_events_3_ports(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_3)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_3
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi()))
def test_modules_mgmt_poller_events_single_port(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_1)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_1
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports))):
#with patch('builtins.open', MagicMock(side_effect=self.mock_open_new_side_effect_poller_test)):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_normal_warm_reboot(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_warm_reboot
# mock /proc/cmdline with warm reboot boot type key value
mock_file_content[modules_mgmt.PROC_CMDLINE] = f'{modules_mgmt.CMDLINE_STR_TO_LOOK_FOR}{modules_mgmt.CMDLINE_VAL_TO_LOOK_FOR}'
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_1)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_1
# set the port to start with FW controlled before warm reboot takes place
mock_file_content[modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("0")] = "0"
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPoller(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports, warm_reboot=True))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_plug_out_fw_cable_after_warm_reboot(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_warm_reboot
# mock /proc/cmdline with warm reboot boot type key value
mock_file_content[modules_mgmt.PROC_CMDLINE] = f'{modules_mgmt.CMDLINE_STR_TO_LOOK_FOR}{modules_mgmt.CMDLINE_VAL_TO_LOOK_FOR}'
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_1)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_1
# set the port to start with FW controlled before warm reboot takes place
mock_file_content[modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("0")] = "0"
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPoller(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports, port_plug_out=True, warm_reboot=True))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_plug_out_plug_in_fw_cable_after_warm_reboot(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_warm_reboot
# mock /proc/cmdline with warm reboot boot type key value
mock_file_content[modules_mgmt.PROC_CMDLINE] = f'{modules_mgmt.CMDLINE_STR_TO_LOOK_FOR}{modules_mgmt.CMDLINE_VAL_TO_LOOK_FOR}'
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_1)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_1
mock_file_content[modules_mgmt.SYSFS_INDEPENDENT_FD_FW_CONTROL.format("0")] = "0"
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPoller(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports, port_plug_out=True, warm_reboot=True, port_plug_in=True))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_no_ports(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=0)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == 0
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_ports_disconnected(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_3)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_3
# update hw_present sysfs with value of 0 for each port
for i in range(num_of_tested_ports):
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format(f"{i}")
mock_file_content[modules_sysfs] = "0"
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports, ports_connected=False))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_bad_flows_port_disconnected(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_1)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_1
# update hw_present sysfs with value of 0 for each port
for i in range(num_of_tested_ports):
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format(f"{i}")
mock_file_content[modules_sysfs] = "0"
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports, ports_connected=False))):
self.modules_mgmt_thrd.run()
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_bad_flows_power_good(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_1)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_1
# update power_good sysfs with value of 0 for each port
for i in range(num_of_tested_ports):
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format(f"{i}")
mock_file_content[modules_sysfs] = "0"
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports, ports_connected=False))):
self.modules_mgmt_thrd.run()
for i in range(num_of_tested_ports):
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format(f"{i}")
mock_file_content[modules_sysfs] = "1"
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi(False, True)))
def test_modules_mgmt_bad_flows_ports_powered_off_fw_controlled(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_32)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_32
# create or update different sysfs and is_file mocking with relevant value for each port
for i in range(num_of_tested_ports):
# mock power_on sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format(f"{i}")
mock_file_content[modules_sysfs] = "0"
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = 300 + i
# mock hw_presence sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format(f'{i}')
mock_file_content[modules_sysfs] = "1"
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = i
# mock power_good sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format(f'{i}')
mock_file_content[modules_sysfs] = "1"
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = 200 + i
# mock hw_reset sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_HW_RESET.format(f'{i}')
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = 400 + i
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports, fw_controlled_ports=True))):
self.modules_mgmt_thrd.run()
# change power_on sysfs values back to the default ones
for i in range(num_of_tested_ports):
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format(f"{i}")
mock_file_content[modules_sysfs] = "1"
@patch('os.path.isfile', MagicMock(side_effect=mock_is_file_indep_mode_enabled))
@patch('builtins.open', spec=open)
@patch('sonic_platform.sfp.SFP', MagicMock(return_value=MockSFPxcvrapi()))
def test_modules_mgmt_bad_flows_ports_powered_off_sw_controlled(self, mock_open):
mock_open.side_effect = self.mock_open_new_side_effect_poller_test
DeviceDataManager.get_sfp_count = mock.MagicMock(return_value=DEFAULT_NUM_OF_PORTS_32)
num_of_tested_ports = DeviceDataManager.get_sfp_count()
assert num_of_tested_ports == DEFAULT_NUM_OF_PORTS_32
# create or update different sysfs and is_file mocking with relevant value for each port
for i in range(num_of_tested_ports):
# mock power_on sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format(f"{i}")
mock_file_content[modules_sysfs] = "0"
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = 300 + i
# mock hw_presence sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_PRESENCE.format(f'{i}')
mock_file_content[modules_sysfs] = "1"
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = i
# mock power_good sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_GOOD.format(f'{i}')
mock_file_content[modules_sysfs] = "1"
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = 200 + i
# mock hw_reset sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_HW_RESET.format(f'{i}')
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = 400 + i
# mock frequency_support sysfs for all ports
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_FREQ_SUPPORT.format(f'{i}')
mock_file_content[modules_sysfs] = "0"
mock_is_file_indep_mode_enabled_content[modules_sysfs] = True
self.fd_number_by_fd_name_dict[modules_sysfs] = 600 + i
# start modules_mgmt thread and the test in poller part
with patch('select.poll', MagicMock(return_value=MockPollerStopEvent(self.modules_mgmt_task_stopping_event
, self.modules_mgmt_thrd, num_of_tested_ports))):
self.modules_mgmt_thrd.run()
# change power_on sysfs values back to the default ones
for i in range(num_of_tested_ports):
modules_sysfs = modules_mgmt.SYSFS_INDEPENDENT_FD_POWER_ON.format(f"{i}")
mock_file_content[modules_sysfs] = "1"
def tearDown(cls):
mock_file_content[modules_mgmt.PROC_CMDLINE] = ''
cls.modules_mgmt_thrd = None
# a check that modules mgmt thread ran and got into the poller part where the tests here has all checks
assert POLLER_EXECUTED