Skip to content
Draft
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
6 changes: 6 additions & 0 deletions cloudshell/networking/cisco/command_actions/iface_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
iface,
)

# Pattern to match VLAN 1 configuration (default state that should be preserved)
VLAN_1_PATTERN = r"^\s*switchport\s+trunk\s+allowed\s+vlan\s+1\s*$"


class IFaceActions:
def __init__(self, cli_service, logger):
Expand Down Expand Up @@ -114,6 +117,9 @@ def clean_interface_switchport_config(

for line in current_config.splitlines():
if line.strip(" ").startswith("switchport "):
# Skip removing "switchport trunk allowed vlan 1" as it's the default state
if re.match(VLAN_1_PATTERN, line, re.IGNORECASE):
continue
line_to_remove = re.sub(r"\s+\d+[-\d+,]+", "", line).strip(" ")
CommandTemplateExecutor(
self._cli_service,
Expand Down
100 changes: 100 additions & 0 deletions tests/networking/cisco/command_actions/test_iface_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,103 @@ def test_get_no_l2_protocol_tunnel_cmd(self, vlan_templates_mock, cte_mock):
error_map=None,
)
self.assertEqual(result, cte_mock.return_value)

@patch(
"cloudshell.networking.cisco.command_actions.iface_actions"
".CommandTemplateExecutor"
)
def test_clean_interface_switchport_config_preserves_vlan_1(self, cte_mock):
"""Test that switchport trunk allowed vlan 1 is not removed (default state)."""
current_config = """Building configuration...

Current configuration : 144 bytes
!
interface GigabitEthernet110/1/0/6
description KG-255X-06-PT
switchport
switchport trunk allowed vlan 1
switchport mode dynamic auto
end
"""
executor_mock = MagicMock()
cte_mock.return_value = executor_mock

self._handler.clean_interface_switchport_config(current_config)

# Verify that execute_command was called for other switchport lines but not for vlan 1
calls = executor_mock.execute_command.call_args_list
# Should be called once for "switchport mode dynamic auto"
# but NOT for "switchport trunk allowed vlan 1"
# Note: "switchport" alone (without trailing space) is not matched by the pattern
self.assertEqual(len(calls), 1)

# Verify the command that was issued
called_commands = [call[1]["command"] for call in calls]
self.assertIn("switchport mode dynamic auto", called_commands)
# Ensure vlan 1 line was NOT processed (would appear as "switchport trunk allowed vlan" after regex)
for cmd in called_commands:
self.assertNotIn("trunk allowed vlan", cmd)

@patch(
"cloudshell.networking.cisco.command_actions.iface_actions"
".CommandTemplateExecutor"
)
def test_clean_interface_switchport_config_removes_other_vlans(self, cte_mock):
"""Test that switchport trunk allowed vlan with other VLANs are removed."""
current_config = """Building configuration...

Current configuration : 144 bytes
!
interface GigabitEthernet110/1/0/6
description KG-255X-06-PT
switchport
switchport trunk allowed vlan 100
switchport mode trunk
end
"""
executor_mock = MagicMock()
cte_mock.return_value = executor_mock

self._handler.clean_interface_switchport_config(current_config)

# Verify that execute_command was called for all switchport lines including vlan 100
calls = executor_mock.execute_command.call_args_list
# Should be called twice: "switchport trunk allowed vlan", "switchport mode trunk"
# Note: "switchport" alone (without trailing space) is not matched by the pattern
self.assertEqual(len(calls), 2)

# Verify the commands that were issued
called_commands = [call[1]["command"] for call in calls]
self.assertIn("switchport trunk allowed vlan", called_commands)
self.assertIn("switchport mode trunk", called_commands)

@patch(
"cloudshell.networking.cisco.command_actions.iface_actions"
".CommandTemplateExecutor"
)
def test_clean_interface_switchport_config_removes_vlan_1_in_range(self, cte_mock):
"""Test that VLAN 1 in a range or list is still removed (not default state)."""
current_config = """Building configuration...

Current configuration : 144 bytes
!
interface GigabitEthernet110/1/0/6
description Test Port
switchport trunk allowed vlan 1,2,3
switchport mode trunk
end
"""
executor_mock = MagicMock()
cte_mock.return_value = executor_mock

self._handler.clean_interface_switchport_config(current_config)

# Verify that execute_command was called for VLAN range including vlan 1
calls = executor_mock.execute_command.call_args_list
# Should be called twice: "switchport trunk allowed vlan", "switchport mode trunk"
self.assertEqual(len(calls), 2)

# Verify the commands that were issued - vlan 1,2,3 should be removed
called_commands = [call[1]["command"] for call in calls]
self.assertIn("switchport trunk allowed vlan", called_commands)
self.assertIn("switchport mode trunk", called_commands)