diff --git a/fboss/cli/fboss2/commands/config/interface/switchport/access/vlan/CmdConfigInterfaceSwitchportAccessVlan.cpp b/fboss/cli/fboss2/commands/config/interface/switchport/access/vlan/CmdConfigInterfaceSwitchportAccessVlan.cpp index 2ab6a2bf0714e..cf035e32d69e9 100644 --- a/fboss/cli/fboss2/commands/config/interface/switchport/access/vlan/CmdConfigInterfaceSwitchportAccessVlan.cpp +++ b/fboss/cli/fboss2/commands/config/interface/switchport/access/vlan/CmdConfigInterfaceSwitchportAccessVlan.cpp @@ -31,6 +31,21 @@ CmdConfigInterfaceSwitchportAccessVlan::queryClient( // Extract the VLAN ID (validation already done in VlanIdValue constructor) int32_t vlanId = vlanIdValue.getVlanId(); + // Validate that the VLAN exists before modifying config + auto& config = ConfigSession::getInstance().getAgentConfig(); + bool vlanExists = false; + for (const auto& vlan : *config.sw()->vlans()) { + if (*vlan.id() == vlanId) { + vlanExists = true; + break; + } + } + if (!vlanExists) { + throw std::invalid_argument( + "VLAN " + std::to_string(vlanId) + + " does not exist. Create the VLAN first before assigning ports to it."); + } + // Collect the logical port IDs we need to update std::unordered_set portIds; @@ -44,7 +59,6 @@ CmdConfigInterfaceSwitchportAccessVlan::queryClient( } // Also update the vlanPorts entries for these ports - auto& config = ConfigSession::getInstance().getAgentConfig(); auto& vlanPorts = *config.sw()->vlanPorts(); for (auto& vlanPort : vlanPorts) { if (portIds.count(*vlanPort.logicalPort())) { diff --git a/fboss/cli/fboss2/test/config/CmdConfigInterfaceSwitchportAccessVlanTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigInterfaceSwitchportAccessVlanTest.cpp index ae336dbe32392..915134089b11e 100644 --- a/fboss/cli/fboss2/test/config/CmdConfigInterfaceSwitchportAccessVlanTest.cpp +++ b/fboss/cli/fboss2/test/config/CmdConfigInterfaceSwitchportAccessVlanTest.cpp @@ -52,6 +52,17 @@ class CmdConfigInterfaceSwitchportAccessVlanTestFixture "spanningTreeState": 2, "emitTags": false } + ], + "defaultVlan": 4000, + "vlans": [ + {"id": 1, "name": "vlan1", "routable": true, "intfID": 1}, + {"id": 2001, "name": "vlan2001", "routable": true, "intfID": 2001}, + {"id": 3000, "name": "vlan3000", "routable": true, "intfID": 0}, + {"id": 4000, "name": "vlan4000", "routable": false, "intfID": 0} + ], + "interfaces": [ + {"intfID": 1, "vlanID": 1, "routerID": 0, "type": 1, "mtu": 9412}, + {"intfID": 2001, "vlanID": 2001, "routerID": 0, "type": 1, "mtu": 9412} ] } })") {} @@ -191,4 +202,28 @@ TEST_F( std::invalid_argument); } +TEST_F( + CmdConfigInterfaceSwitchportAccessVlanTestFixture, + queryClientThrowsWhenVlanDoesNotExist) { + auto cmd = CmdConfigInterfaceSwitchportAccessVlan(); + utils::InterfaceList interfaces({"eth1/1/1"}); + VlanIdValue vlanId({"4094"}); + + try { + cmd.queryClient(localhost(), interfaces, vlanId); + FAIL() << "Expected std::invalid_argument"; + } catch (const std::invalid_argument& e) { + std::string msg = e.what(); + EXPECT_THAT(msg, HasSubstr("VLAN 4094")); + EXPECT_THAT(msg, HasSubstr("does not exist")); + } + + auto& config = ConfigSession::getInstance().getAgentConfig(); + for (const auto& port : *config.sw()->ports()) { + if (*port.name() == "eth1/1/1") { + EXPECT_EQ(*port.ingressVlan(), 1); + } + } +} + } // namespace facebook::fboss