Skip to content

Commit 3e3daf3

Browse files
Exclude Smart Switch from modular chassis operations/checks (#3988)
Why I did it When running the test_fwutil_update_current test in https://github.com/sonic-net/sonic-mgmt/blob/master/tests/platform_tests/fwutil/test_fwutil.py we encounter the following issue when we run the command admin@sonic:$ sudo fwutil update chassis component ONIE fw -y Error: Failed to parse "platform_components.json": invalid platform schema: "module" key hasn't been found. Aborting... Aborted! This is because platform_components.json for smartswitch doesn't have module key in it and this should be ignored for smartswitch. What I did Avoid any modular chassis operations on smart-switch. How I did it Added a check to verify the device is not a smart switch before treating it as a modular chassis.
1 parent 036e4e6 commit 3e3daf3

File tree

2 files changed

+157
-2
lines changed

2 files changed

+157
-2
lines changed

fwutil/lib.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ def get_platform(self):
197197
def get_chassis(self):
198198
return self.__chassis
199199

200+
def is_smart_switch(self):
201+
return self.__chassis.is_smartswitch()
202+
200203
def is_modular_chassis(self):
201204
return len(self.module_component_map) > 0
202205

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

537540
self.__root_path = root_path
538-
539-
self.__pcp = PlatformComponentsParser(self.is_modular_chassis())
541+
is_modular_chassis = self.is_modular_chassis() and not self.is_smart_switch()
542+
self.__pcp = PlatformComponentsParser(is_modular_chassis)
540543
self.__pcp.parse_platform_components(root_path)
541544

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

553+
if diff_keys and section == self.SECTION_MODULE and self.is_smart_switch():
554+
return
555+
550556
if diff_keys:
551557
raise RuntimeError(
552558
"{} names mismatch: keys={}".format(

tests/fwutil_test.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,154 @@ def test_is_capable_auto_update(self):
9292
assert CUProvider.is_capable_auto_update('none') == True
9393
assert CUProvider.is_capable_auto_update('def') == True
9494

95+
@patch('fwutil.lib.Platform')
96+
@patch('fwutil.lib.PlatformComponentsParser')
97+
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
98+
@patch('os.path.isdir', return_value=True)
99+
def test_is_smart_switch_method(self, mock_isdir, mock_validate,
100+
mock_parser_class, mock_platform_class):
101+
"""Test that the is_smart_switch method correctly returns True
102+
when the chassis.is_smartswitch() method returns True."""
103+
# Setup mock chassis
104+
mock_chassis = MagicMock()
105+
mock_chassis.is_smartswitch.return_value = True
106+
107+
# Setup mock platform
108+
mock_platform = MagicMock()
109+
mock_platform.get_chassis.return_value = mock_chassis
110+
mock_platform_class.return_value = mock_platform
111+
112+
# Create ComponentUpdateProvider instance
113+
cup = fwutil_lib.ComponentUpdateProvider()
114+
115+
# Test is_smart_switch method
116+
assert cup.is_smart_switch()
117+
mock_chassis.is_smartswitch.assert_called_once()
118+
119+
@patch('fwutil.lib.Platform')
120+
@patch('fwutil.lib.PlatformComponentsParser')
121+
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
122+
@patch('os.mkdir')
123+
def test_smartswitch_modular_chassis_parsing(self, mock_mkdir, mock_validate,
124+
mock_parser_class, mock_platform_class):
125+
"""Test that SmartSwitch devices with modules are passed as non-modular (False)
126+
to the PlatformComponentsParser constructor."""
127+
# Setup mock chassis that is SmartSwitch and has modules
128+
mock_chassis = MagicMock()
129+
mock_chassis.is_smartswitch.return_value = True
130+
mock_chassis.get_all_modules.return_value = [MagicMock(), MagicMock()] # 2 modules
131+
132+
# Setup mock platform
133+
mock_platform = MagicMock()
134+
mock_platform.get_chassis.return_value = mock_chassis
135+
mock_platform_class.return_value = mock_platform
136+
137+
# Setup mock parser
138+
mock_parser = MagicMock()
139+
mock_parser_class.return_value = mock_parser
140+
141+
# Create ComponentUpdateProvider instance
142+
fwutil_lib.ComponentUpdateProvider()
143+
144+
# Verify that PlatformComponentsParser was called with is_modular_chassis=False
145+
# because SmartSwitch should be treated as non-modular for parsing purposes
146+
mock_parser_class.assert_called_once_with(False)
147+
148+
@patch('fwutil.lib.Platform')
149+
@patch('fwutil.lib.PlatformComponentsParser')
150+
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
151+
@patch('os.mkdir')
152+
def test_regular_modular_chassis_parsing(self, mock_mkdir, mock_validate, mock_parser_class, mock_platform_class):
153+
"""Test that regular modular chassis is treated as modular for parsing"""
154+
# Setup mock chassis that is not SmartSwitch but has modules
155+
mock_chassis = MagicMock()
156+
mock_chassis.is_smartswitch.return_value = False
157+
mock_chassis.get_all_modules.return_value = [MagicMock(), MagicMock()] # 2 modules
158+
159+
# Setup mock platform
160+
mock_platform = MagicMock()
161+
mock_platform.get_chassis.return_value = mock_chassis
162+
mock_platform_class.return_value = mock_platform
163+
164+
# Setup mock parser
165+
mock_parser = MagicMock()
166+
mock_parser_class.return_value = mock_parser
167+
168+
# Create ComponentUpdateProvider instance
169+
fwutil_lib.ComponentUpdateProvider()
170+
171+
# Verify that PlatformComponentsParser was called with is_modular_chassis=True
172+
# because regular modular chassis should be treated as modular
173+
mock_parser_class.assert_called_once_with(True)
174+
175+
@patch('fwutil.lib.Platform')
176+
@patch('fwutil.lib.PlatformComponentsParser')
177+
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
178+
@patch('os.mkdir')
179+
def test_smartswitch_module_validation_skip(self, mock_mkdir, mock_validate,
180+
mock_parser_class, mock_platform_class):
181+
"""Test that module validation is skipped for SmartSwitch platforms"""
182+
# Setup mock chassis that is SmartSwitch
183+
mock_chassis = MagicMock()
184+
mock_chassis.is_smartswitch.return_value = True
185+
mock_chassis.get_all_modules.return_value = [MagicMock()] # Has modules
186+
187+
# Setup mock platform
188+
mock_platform = MagicMock()
189+
mock_platform.get_chassis.return_value = mock_chassis
190+
mock_platform_class.return_value = mock_platform
191+
192+
# Setup mock parser
193+
mock_parser = MagicMock()
194+
mock_parser_class.return_value = mock_parser
195+
196+
# Create ComponentUpdateProvider instance
197+
cup = fwutil_lib.ComponentUpdateProvider()
198+
199+
# Test that module validation is skipped for SmartSwitch
200+
# This should not raise an exception even if there are differences
201+
pdp_map = {'module1': {'comp1': MagicMock()}}
202+
pcp_map = {'module2': {'comp2': MagicMock()}} # Different modules
203+
204+
# Should not raise exception for SmartSwitch module validation
205+
cup._ComponentUpdateProvider__validate_component_map(
206+
cup.SECTION_MODULE, pdp_map, pcp_map
207+
)
208+
209+
@patch('fwutil.lib.Platform')
210+
@patch('fwutil.lib.PlatformComponentsParser')
211+
@patch('fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema')
212+
@patch('os.mkdir')
213+
def test_regular_chassis_module_validation_error(self, mock_mkdir, mock_validate,
214+
mock_parser_class, mock_platform_class):
215+
"""Test that module validation raises error for regular modular chassis"""
216+
# Setup mock chassis that is not SmartSwitch but has modules
217+
mock_chassis = MagicMock()
218+
mock_chassis.is_smartswitch.return_value = False
219+
mock_chassis.get_all_modules.return_value = [MagicMock()] # Has modules
220+
221+
# Setup mock platform
222+
mock_platform = MagicMock()
223+
mock_platform.get_chassis.return_value = mock_chassis
224+
mock_platform_class.return_value = mock_platform
225+
226+
# Setup mock parser
227+
mock_parser = MagicMock()
228+
mock_parser_class.return_value = mock_parser
229+
230+
# Create ComponentUpdateProvider instance
231+
cup = fwutil_lib.ComponentUpdateProvider()
232+
233+
# Test that module validation raises error for regular modular chassis
234+
pdp_map = {'module1': {'comp1': MagicMock()}}
235+
pcp_map = {'module2': {'comp2': MagicMock()}} # Different modules
236+
237+
# Should raise exception for regular modular chassis
238+
with pytest.raises(RuntimeError) as excinfo:
239+
cup._ComponentUpdateProvider__validate_component_map(
240+
cup.SECTION_MODULE, pdp_map, pcp_map
241+
)
242+
assert "Module names mismatch" in str(excinfo.value)
243+
95244
def teardown(self):
96245
print('TEARDOWN')

0 commit comments

Comments
 (0)