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
10 changes: 8 additions & 2 deletions fwutil/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ def get_platform(self):
def get_chassis(self):
return self.__chassis

def is_smart_switch(self):
return self.__chassis.is_smartswitch()

def is_modular_chassis(self):
return len(self.module_component_map) > 0

Expand Down Expand Up @@ -535,8 +538,8 @@ def __init__(self, root_path=None):
os.mkdir(FIRMWARE_AU_STATUS_DIR)

self.__root_path = root_path

self.__pcp = PlatformComponentsParser(self.is_modular_chassis())
is_modular_chassis = self.is_modular_chassis() and not self.is_smart_switch()
self.__pcp = PlatformComponentsParser(is_modular_chassis)
self.__pcp.parse_platform_components(root_path)

self.__validate_platform_schema(self.__pcp)
Expand All @@ -547,6 +550,9 @@ def __diff_keys(self, keys1, keys2):
def __validate_component_map(self, section, pdp_map, pcp_map):
diff_keys = self.__diff_keys(list(pdp_map.keys()), list(pcp_map.keys()))

if diff_keys and section == self.SECTION_MODULE and self.is_smart_switch():
return

if diff_keys:
raise RuntimeError(
"{} names mismatch: keys={}".format(
Expand Down
67 changes: 67 additions & 0 deletions scripts/generate_dump
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,65 @@ save_sys() {
chmod ugo+rw -R $DUMPDIR/$BASE/sys
}

save_sys_depth1() {
trap 'handle_error $? $LINENO' ERR
local src="$1"
local dest="$2"
shift 2
# sys files we do not want to include in dump
local excludes=("$@")

$MKDIR $V -p "$dest"

should_skip() {
local base="$1"
for ex in "${excludes[@]}"; do
[[ "$base" == "$ex" ]] && return 0
done
return 1
}

# Copy files directly under src
for f in "$src"/*; do
if [ -f "$f" ]; then
local base="$(basename "$f")"
should_skip "$base" && continue
local target="$dest/$base"

if $NOOP; then
echo "cat $f > $target 2>/dev/null"
else
cat "$f" > "$target" 2>/dev/null
fi
fi
done

# Copy sub folders
for d in "$src"/*; do
if [ -d "$d" ]; then
local subdir_dest="$dest/$(basename "$d")"
$MKDIR $V -p "$subdir_dest"

# copy only files inside sub folder, not sub-sub folders
for f in "$d"/*; do
if [ -f "$f" ]; then
local base="$(basename "$f")"
should_skip "$base" && continue
local target="$subdir_dest/$base"

if $NOOP; then
echo "cat $f > $target 2>/dev/null"
else
cat "$f" > "$target" 2>/dev/null
fi
fi
done
fi
done

chmod ugo+rw -R "$dest"
}

###############################################################################
# Given list of pstore directories or files, saves subdirectories and files to tar.
# Globals:
Expand Down Expand Up @@ -1405,6 +1464,12 @@ collect_mellanox() {

save_cmd "get_component_versions.py" "component_versions"

local sdk_sysfs_src_path="/sys/module/sx_core/asic0"
local sdk_sysfs_dest_path="$TARDIR/sdk_sysfs/sx_core/asic0"
local excludes_sysfs_files=(rx_los tx_disable module_info module_latched_flag_info power_mode power_mode_policy reinsert reset)

save_sys_depth1 "$sdk_sysfs_src_path" "$sdk_sysfs_dest_path" "${excludes_sysfs_files[@]}" &

# Save CMIS-host-management related files
local cmis_host_mgmt_path="cmis-host-mgmt"

Expand All @@ -1419,6 +1484,8 @@ collect_mellanox() {
fi

save_cmd "show interfaces autoneg status" "autoneg.status"

wait
}

###############################################################################
Expand Down
149 changes: 149 additions & 0 deletions tests/fwutil_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,154 @@ def test_is_capable_auto_update(self):
assert CUProvider.is_capable_auto_update('none') == True
assert CUProvider.is_capable_auto_update('def') == True

@patch('fwutil.lib.Platform')
@patch('fwutil.lib.PlatformComponentsParser')
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
@patch('os.path.isdir', return_value=True)
def test_is_smart_switch_method(self, mock_isdir, mock_validate,
mock_parser_class, mock_platform_class):
"""Test that the is_smart_switch method correctly returns True
when the chassis.is_smartswitch() method returns True."""
# Setup mock chassis
mock_chassis = MagicMock()
mock_chassis.is_smartswitch.return_value = True

# Setup mock platform
mock_platform = MagicMock()
mock_platform.get_chassis.return_value = mock_chassis
mock_platform_class.return_value = mock_platform

# Create ComponentUpdateProvider instance
cup = fwutil_lib.ComponentUpdateProvider()

# Test is_smart_switch method
assert cup.is_smart_switch()
mock_chassis.is_smartswitch.assert_called_once()

@patch('fwutil.lib.Platform')
@patch('fwutil.lib.PlatformComponentsParser')
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
@patch('os.mkdir')
def test_smartswitch_modular_chassis_parsing(self, mock_mkdir, mock_validate,
mock_parser_class, mock_platform_class):
"""Test that SmartSwitch devices with modules are passed as non-modular (False)
to the PlatformComponentsParser constructor."""
# Setup mock chassis that is SmartSwitch and has modules
mock_chassis = MagicMock()
mock_chassis.is_smartswitch.return_value = True
mock_chassis.get_all_modules.return_value = [MagicMock(), MagicMock()] # 2 modules

# Setup mock platform
mock_platform = MagicMock()
mock_platform.get_chassis.return_value = mock_chassis
mock_platform_class.return_value = mock_platform

# Setup mock parser
mock_parser = MagicMock()
mock_parser_class.return_value = mock_parser

# Create ComponentUpdateProvider instance
fwutil_lib.ComponentUpdateProvider()

# Verify that PlatformComponentsParser was called with is_modular_chassis=False
# because SmartSwitch should be treated as non-modular for parsing purposes
mock_parser_class.assert_called_once_with(False)

@patch('fwutil.lib.Platform')
@patch('fwutil.lib.PlatformComponentsParser')
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
@patch('os.mkdir')
def test_regular_modular_chassis_parsing(self, mock_mkdir, mock_validate, mock_parser_class, mock_platform_class):
"""Test that regular modular chassis is treated as modular for parsing"""
# Setup mock chassis that is not SmartSwitch but has modules
mock_chassis = MagicMock()
mock_chassis.is_smartswitch.return_value = False
mock_chassis.get_all_modules.return_value = [MagicMock(), MagicMock()] # 2 modules

# Setup mock platform
mock_platform = MagicMock()
mock_platform.get_chassis.return_value = mock_chassis
mock_platform_class.return_value = mock_platform

# Setup mock parser
mock_parser = MagicMock()
mock_parser_class.return_value = mock_parser

# Create ComponentUpdateProvider instance
fwutil_lib.ComponentUpdateProvider()

# Verify that PlatformComponentsParser was called with is_modular_chassis=True
# because regular modular chassis should be treated as modular
mock_parser_class.assert_called_once_with(True)

@patch('fwutil.lib.Platform')
@patch('fwutil.lib.PlatformComponentsParser')
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
@patch('os.mkdir')
def test_smartswitch_module_validation_skip(self, mock_mkdir, mock_validate,
mock_parser_class, mock_platform_class):
"""Test that module validation is skipped for SmartSwitch platforms"""
# Setup mock chassis that is SmartSwitch
mock_chassis = MagicMock()
mock_chassis.is_smartswitch.return_value = True
mock_chassis.get_all_modules.return_value = [MagicMock()] # Has modules

# Setup mock platform
mock_platform = MagicMock()
mock_platform.get_chassis.return_value = mock_chassis
mock_platform_class.return_value = mock_platform

# Setup mock parser
mock_parser = MagicMock()
mock_parser_class.return_value = mock_parser

# Create ComponentUpdateProvider instance
cup = fwutil_lib.ComponentUpdateProvider()

# Test that module validation is skipped for SmartSwitch
# This should not raise an exception even if there are differences
pdp_map = {'module1': {'comp1': MagicMock()}}
pcp_map = {'module2': {'comp2': MagicMock()}} # Different modules

# Should not raise exception for SmartSwitch module validation
cup._ComponentUpdateProvider__validate_component_map(
cup.SECTION_MODULE, pdp_map, pcp_map
)

@patch('fwutil.lib.Platform')
@patch('fwutil.lib.PlatformComponentsParser')
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
@patch('os.mkdir')
def test_regular_chassis_module_validation_error(self, mock_mkdir, mock_validate,
mock_parser_class, mock_platform_class):
"""Test that module validation raises error for regular modular chassis"""
# Setup mock chassis that is not SmartSwitch but has modules
mock_chassis = MagicMock()
mock_chassis.is_smartswitch.return_value = False
mock_chassis.get_all_modules.return_value = [MagicMock()] # Has modules

# Setup mock platform
mock_platform = MagicMock()
mock_platform.get_chassis.return_value = mock_chassis
mock_platform_class.return_value = mock_platform

# Setup mock parser
mock_parser = MagicMock()
mock_parser_class.return_value = mock_parser

# Create ComponentUpdateProvider instance
cup = fwutil_lib.ComponentUpdateProvider()

# Test that module validation raises error for regular modular chassis
pdp_map = {'module1': {'comp1': MagicMock()}}
pcp_map = {'module2': {'comp2': MagicMock()}} # Different modules

# Should raise exception for regular modular chassis
with pytest.raises(RuntimeError) as excinfo:
cup._ComponentUpdateProvider__validate_component_map(
cup.SECTION_MODULE, pdp_map, pcp_map
)
assert "Module names mismatch" in str(excinfo.value)

def teardown(self):
print('TEARDOWN')
Loading