From b8f4ab1d8824609002f35a384861ead71dfe0692 Mon Sep 17 00:00:00 2001 From: Dogface2k <100990646+Dogface2k@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:36:25 +0100 Subject: [PATCH 1/2] OVS: ignore distributed VPCs owned by other providers --- .../network/ovs/OvsTunnelManagerImpl.java | 52 ++-- .../network/ovs/OvsTunnelManagerImplTest.java | 256 ++++++++++++++++++ 2 files changed, 290 insertions(+), 18 deletions(-) create mode 100644 plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java diff --git a/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 804f29c01b1e..4dd9874af3c7 100644 --- a/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -396,6 +396,12 @@ boolean isVpcEnabledForDistributedRouter(long vpcId) { return vpc.usesDistributedRouter(); } + boolean isOvsDistributedRouterVpc(long vpcId) { + VpcVO vpc = _vpcDao.findById(vpcId); + return vpc != null && vpc.usesDistributedRouter() + && _vpcMgr.isProviderSupportServiceInVpc(vpcId, Network.Service.Connectivity, Network.Provider.Ovs); + } + @Override public void checkAndPrepareHostForTunnelNetwork(Network nw, Host host) { if (nw.getVpcId() != null && isVpcEnabledForDistributedRouter(nw.getVpcId())) { @@ -684,10 +690,8 @@ private void handleVmStateChange(VMInstanceVO vm) { } for (Long vpcId: vpcIds) { - VpcVO vpc = _vpcDao.findById(vpcId); - // nothing to do if the VPC is not setup for distributed routing - if (vpc == null || !vpc.usesDistributedRouter()) { - return; + if (!isOvsDistributedRouterVpc(vpcId)) { + continue; } // get the list of hosts on which VPC spans (i.e hosts that need to be aware of VPC topology change update) @@ -754,20 +758,32 @@ OvsVpcPhysicalTopologyConfigCommand prepareVpcTopologyUpdate(long vpcId) { } for (Network network: vpcNetworks) { + if (network.getBroadcastDomainType() != BroadcastDomainType.Vswitch || network.getBroadcastUri() == null) { + throw new CloudRuntimeException(String.format( + "OVS distributed-router VPC %s contains network %s without a Vswitch broadcast URI", + vpc.getUuid(), network.getUuid())); + } String key = network.getBroadcastUri().getAuthority(); - long gre_key; - if (key.contains(".")) { - String[] parts = key.split("\\."); - gre_key = Long.parseLong(parts[1]); - } else { - try { - gre_key = Long.parseLong(BroadcastDomainType.getValue(key)); - } catch (Exception e) { - return null; - } + String[] parts = StringUtils.split(key, '.'); + if (parts == null || parts.length != 2 || !String.valueOf(vpcId).equals(parts[0])) { + throw new CloudRuntimeException(String.format( + "OVS distributed-router network %s has invalid broadcast key %s for VPC %s", + network.getUuid(), key, vpc.getUuid())); + } + long greKey; + try { + greKey = Long.parseLong(parts[1]); + } catch (NumberFormatException e) { + throw new CloudRuntimeException(String.format( + "OVS distributed-router network %s has non-numeric GRE key %s", + network.getUuid(), parts[1]), e); } NicVO nic = _nicDao.findByIp4AddressAndNetworkId(network.getGateway(), network.getId()); - OvsVpcPhysicalTopologyConfigCommand.Tier tier = new OvsVpcPhysicalTopologyConfigCommand.Tier(gre_key, + if (nic == null) { + throw new CloudRuntimeException(String.format( + "Unable to find the gateway NIC for OVS distributed-router network %s", network.getUuid())); + } + OvsVpcPhysicalTopologyConfigCommand.Tier tier = new OvsVpcPhysicalTopologyConfigCommand.Tier(greKey, network.getUuid(), network.getGateway(), nic.getMacAddress(), network.getCidr()); tiers.add(tier); } @@ -802,9 +818,9 @@ public class NetworkAclEventsSubscriber implements MessageSubscriber { public void onPublishMessage(String senderAddress, String subject, Object args) { try { NetworkVO network = (NetworkVO) args; - String bridgeName=generateBridgeNameForVpc(network.getVpcId()); - if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) { - long vpcId = network.getVpcId(); + Long vpcId = network.getVpcId(); + if (vpcId != null && isOvsDistributedRouterVpc(vpcId)) { + String bridgeName = generateBridgeNameForVpc(vpcId); OvsVpcRoutingPolicyConfigCommand cmd = prepareVpcRoutingPolicyUpdate(vpcId); cmd.setSequenceNumber(getNextRoutingPolicyUpdateSequenceNumber(vpcId)); diff --git a/plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java b/plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java new file mode 100644 index 000000000000..982264cd3466 --- /dev/null +++ b/plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java @@ -0,0 +1,256 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +package com.cloud.network.ovs; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand; +import com.cloud.host.dao.HostDao; +import com.cloud.network.Network; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.ovs.dao.VpcDistributedRouterSeqNoDao; +import com.cloud.network.vpc.VpcManager; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.StateMachine2; +import com.cloud.vm.NicVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.VMInstanceDao; + +public class OvsTunnelManagerImplTest { + private static final long VPC_ID = 7L; + private static final long SECOND_VPC_ID = 8L; + + private OvsTunnelManagerImpl manager; + private VpcDao vpcDao; + private VpcManager vpcManager; + private OvsNetworkTopologyGuru topologyGuru; + private NicDao nicDao; + + @Before + public void setUp() { + manager = new OvsTunnelManagerImpl(); + vpcDao = mock(VpcDao.class); + vpcManager = mock(VpcManager.class); + topologyGuru = mock(OvsNetworkTopologyGuru.class); + nicDao = mock(NicDao.class); + manager._vpcDao = vpcDao; + manager._vpcMgr = vpcManager; + manager._ovsNetworkToplogyGuru = topologyGuru; + manager._nicDao = nicDao; + manager._hostDao = mock(HostDao.class); + manager._vmInstanceDao = mock(VMInstanceDao.class); + manager._networkDao = mock(NetworkDao.class); + manager._vpcDrSeqNoDao = mock(VpcDistributedRouterSeqNoDao.class); + manager._agentMgr = mock(AgentManager.class); + } + + @Test + public void testIsOvsDistributedRouterVpcReturnsFalseWhenVpcIsMissing() { + assertFalse(manager.isOvsDistributedRouterVpc(VPC_ID)); + verify(vpcManager, never()).isProviderSupportServiceInVpc(anyLong(), + org.mockito.ArgumentMatchers.any(Network.Service.class), + org.mockito.ArgumentMatchers.any(Network.Provider.class)); + } + + @Test + public void testIsOvsDistributedRouterVpcReturnsFalseWhenVpcIsNotDistributed() { + VpcVO vpc = mock(VpcVO.class); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.usesDistributedRouter()).thenReturn(false); + + assertFalse(manager.isOvsDistributedRouterVpc(VPC_ID)); + } + + @Test + public void testIsOvsDistributedRouterVpcReturnsFalseForNsxDistributedVpc() { + VpcVO vpc = mock(VpcVO.class); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.usesDistributedRouter()).thenReturn(true); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(false); + + assertFalse(manager.isOvsDistributedRouterVpc(VPC_ID)); + } + + @Test + public void testIsOvsDistributedRouterVpcReturnsTrueForOvsConnectivityDistributedVpc() { + VpcVO vpc = mock(VpcVO.class); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.usesDistributedRouter()).thenReturn(true); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(true); + + assertTrue(manager.isOvsDistributedRouterVpc(VPC_ID)); + } + + @Test + public void testPostStateTransitionEventIgnoresNsxDistributedVpc() { + VpcVO vpc = mock(VpcVO.class); + VMInstanceVO vm = mock(VMInstanceVO.class); + @SuppressWarnings("unchecked") + StateMachine2.Transition transition = mock(StateMachine2.Transition.class); + when(vm.getId()).thenReturn(11L); + when(topologyGuru.getVpcIdsVmIsPartOf(11L)).thenReturn(List.of(VPC_ID)); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.usesDistributedRouter()).thenReturn(true); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(false); + when(transition.getCurrentState()).thenReturn(VirtualMachine.State.Starting); + when(transition.getEvent()).thenReturn(VirtualMachine.Event.OperationSucceeded); + when(transition.getToState()).thenReturn(VirtualMachine.State.Running); + + assertTrue(manager.postStateTransitionEvent(transition, vm, true, null)); + + verify(topologyGuru, never()).getVpcSpannedHosts(anyLong()); + verify(vpcManager, never()).getVpcNetworks(anyLong()); + } + + @Test + public void testPostStateTransitionEventContinuesAfterNonOvsVpc() { + VpcVO firstVpc = mock(VpcVO.class); + VpcVO secondVpc = mock(VpcVO.class); + VMInstanceVO vm = mock(VMInstanceVO.class); + @SuppressWarnings("unchecked") + StateMachine2.Transition transition = mock(StateMachine2.Transition.class); + when(vm.getId()).thenReturn(11L); + when(topologyGuru.getVpcIdsVmIsPartOf(11L)).thenReturn(List.of(VPC_ID, SECOND_VPC_ID)); + when(vpcDao.findById(VPC_ID)).thenReturn(firstVpc); + when(vpcDao.findById(SECOND_VPC_ID)).thenReturn(secondVpc); + when(firstVpc.usesDistributedRouter()).thenReturn(true); + when(secondVpc.usesDistributedRouter()).thenReturn(true); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(false); + when(vpcManager.isProviderSupportServiceInVpc(SECOND_VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(false); + when(transition.getCurrentState()).thenReturn(VirtualMachine.State.Starting); + when(transition.getEvent()).thenReturn(VirtualMachine.Event.OperationSucceeded); + when(transition.getToState()).thenReturn(VirtualMachine.State.Running); + + assertTrue(manager.postStateTransitionEvent(transition, vm, true, null)); + + verify(vpcDao).findById(SECOND_VPC_ID); + } + + @Test + public void testNetworkAclSubscriberIgnoresNsxDistributedVpc() { + VpcVO vpc = mock(VpcVO.class); + NetworkVO network = mock(NetworkVO.class); + when(network.getVpcId()).thenReturn(VPC_ID); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.usesDistributedRouter()).thenReturn(true); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(false); + + manager.new NetworkAclEventsSubscriber().onPublishMessage("sender", "Network_ACL_Replaced", network); + + verify(topologyGuru, never()).getVpcSpannedHosts(anyLong()); + verify(vpcManager, never()).getVpcNetworks(anyLong()); + } + + @Test + public void testPrepareVpcTopologyUpdateRejectsNonVswitchTier() { + VpcVO vpc = mock(VpcVO.class); + Network network = mock(Network.class); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.getUuid()).thenReturn("vpc-uuid"); + doReturn(List.of(network)).when(vpcManager).getVpcNetworks(VPC_ID); + when(topologyGuru.getVpcSpannedHosts(VPC_ID)).thenReturn(Collections.emptyList()); + when(topologyGuru.getAllActiveVmsInVpc(VPC_ID)).thenReturn(Collections.emptyList()); + when(network.getUuid()).thenReturn("network-uuid"); + when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.NSX); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + } + + @Test + public void testPrepareVpcTopologyUpdateRejectsBroadcastKeyForAnotherVpc() { + Network network = prepareVswitchNetwork("8.123"); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + + verify(nicDao, never()).findByIp4AddressAndNetworkId("10.0.1.1", 13L); + } + + @Test + public void testPrepareVpcTopologyUpdateRejectsNonNumericGreKey() { + prepareVswitchNetwork("7.invalid"); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + } + + @Test + public void testPrepareVpcTopologyUpdateRejectsMissingGatewayNic() { + prepareVswitchNetwork("7.123"); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + } + + @Test + public void testPrepareVpcTopologyUpdateBuildsValidOvsTopology() { + prepareVswitchNetwork("7.123"); + NicVO gatewayNic = mock(NicVO.class); + when(nicDao.findByIp4AddressAndNetworkId("10.0.1.1", 13L)).thenReturn(gatewayNic); + when(gatewayNic.getMacAddress()).thenReturn("02:00:00:00:00:01"); + + OvsVpcPhysicalTopologyConfigCommand command = manager.prepareVpcTopologyUpdate(VPC_ID); + + String topology = command.getVpcConfigInJson(); + assertTrue(topology.contains("\"grekey\":123")); + assertTrue(topology.contains("\"networkuuid\":\"network-uuid\"")); + assertTrue(topology.contains("\"gatewaymac\":\"02:00:00:00:00:01\"")); + } + + private Network prepareVswitchNetwork(String broadcastKey) { + VpcVO vpc = mock(VpcVO.class); + Network network = mock(Network.class); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.getUuid()).thenReturn("vpc-uuid"); + when(vpc.getCidr()).thenReturn("10.0.0.0/16"); + doReturn(List.of(network)).when(vpcManager).getVpcNetworks(VPC_ID); + when(topologyGuru.getVpcSpannedHosts(VPC_ID)).thenReturn(Collections.emptyList()); + when(topologyGuru.getAllActiveVmsInVpc(VPC_ID)).thenReturn(Collections.emptyList()); + when(network.getId()).thenReturn(13L); + when(network.getUuid()).thenReturn("network-uuid"); + when(network.getGateway()).thenReturn("10.0.1.1"); + when(network.getCidr()).thenReturn("10.0.1.0/24"); + when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.Vswitch); + when(network.getBroadcastUri()).thenReturn(BroadcastDomainType.Vswitch.toUri(broadcastKey)); + return network; + } +} From 42863fdf9ba2d6a720586bcefa79b00d28f246bd Mon Sep 17 00:00:00 2001 From: Dogface2k <100990646+Dogface2k@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:48:09 +0100 Subject: [PATCH 2/2] OVS: contain malformed topology callback failures --- .../network/ovs/OvsTunnelManagerImpl.java | 39 +++--- .../network/ovs/OvsTunnelManagerImplTest.java | 123 ++++++++++++++++++ 2 files changed, 145 insertions(+), 17 deletions(-) diff --git a/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 4dd9874af3c7..3247a43c817c 100644 --- a/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -694,19 +694,24 @@ private void handleVmStateChange(VMInstanceVO vm) { continue; } - // get the list of hosts on which VPC spans (i.e hosts that need to be aware of VPC topology change update) - List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); - String bridgeName=generateBridgeNameForVpc(vpcId); - - OvsVpcPhysicalTopologyConfigCommand topologyConfigCommand = prepareVpcTopologyUpdate(vpcId); - topologyConfigCommand.setSequenceNumber(getNextTopologyUpdateSequenceNumber(vpcId)); - - // send topology change update to VPC spanned hosts - for (Long id: vpcSpannedHostIds) { - if (!sendVpcTopologyChangeUpdate(topologyConfigCommand, id, bridgeName)) { - logger.debug("Failed to send VPC topology change update to host : " + id + ". Moving on " + - "with rest of the host update."); + try { + // get the list of hosts on which VPC spans (i.e hosts that need to be aware of VPC topology change update) + List vpcSpannedHostIds = _ovsNetworkToplogyGuru.getVpcSpannedHosts(vpcId); + String bridgeName=generateBridgeNameForVpc(vpcId); + + OvsVpcPhysicalTopologyConfigCommand topologyConfigCommand = prepareVpcTopologyUpdate(vpcId); + topologyConfigCommand.setSequenceNumber(getNextTopologyUpdateSequenceNumber(vpcId)); + + // send topology change update to VPC spanned hosts + for (Long id: vpcSpannedHostIds) { + if (!sendVpcTopologyChangeUpdate(topologyConfigCommand, id, bridgeName)) { + logger.debug("Failed to send VPC topology change update to host : " + id + ". Moving on " + + "with rest of the host update."); + } } + } catch (RuntimeException e) { + logger.error("Failed to update OVS distributed-router topology for VPC {} after VM {} changed state", + vpcId, vm.getId(), e); } } } @@ -764,19 +769,19 @@ OvsVpcPhysicalTopologyConfigCommand prepareVpcTopologyUpdate(long vpcId) { vpc.getUuid(), network.getUuid())); } String key = network.getBroadcastUri().getAuthority(); - String[] parts = StringUtils.split(key, '.'); - if (parts == null || parts.length != 2 || !String.valueOf(vpcId).equals(parts[0])) { + String expectedPrefix = vpcId + "."; + if (key == null || !key.startsWith(expectedPrefix) || key.indexOf('.', expectedPrefix.length()) >= 0) { throw new CloudRuntimeException(String.format( "OVS distributed-router network %s has invalid broadcast key %s for VPC %s", network.getUuid(), key, vpc.getUuid())); } - long greKey; + int greKey; try { - greKey = Long.parseLong(parts[1]); + greKey = Integer.parseInt(key.substring(expectedPrefix.length())); } catch (NumberFormatException e) { throw new CloudRuntimeException(String.format( "OVS distributed-router network %s has non-numeric GRE key %s", - network.getUuid(), parts[1]), e); + network.getUuid(), key.substring(expectedPrefix.length())), e); } NicVO nic = _nicDao.findByIp4AddressAndNetworkId(network.getGateway(), network.getId()); if (nic == null) { diff --git a/plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java b/plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java index 982264cd3466..a1dbdd403e7d 100644 --- a/plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java +++ b/plugins/network-elements/ovs/src/test/java/com/cloud/network/ovs/OvsTunnelManagerImplTest.java @@ -41,6 +41,7 @@ import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.ovs.dao.VpcDistributedRouterSeqNoDao; +import com.cloud.network.ovs.dao.VpcDistributedRouterSeqNoVO; import com.cloud.network.vpc.VpcManager; import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.dao.VpcDao; @@ -61,6 +62,7 @@ public class OvsTunnelManagerImplTest { private VpcManager vpcManager; private OvsNetworkTopologyGuru topologyGuru; private NicDao nicDao; + private VpcDistributedRouterSeqNoVO sequenceNumber; @Before public void setUp() { @@ -167,6 +169,68 @@ public void testPostStateTransitionEventContinuesAfterNonOvsVpc() { verify(vpcDao).findById(SECOND_VPC_ID); } + @Test + public void testPostStateTransitionEventContainsMalformedOvsTopologyAndContinues() { + VpcVO firstVpc = mock(VpcVO.class); + VpcVO secondVpc = mock(VpcVO.class); + VMInstanceVO vm = mock(VMInstanceVO.class); + @SuppressWarnings("unchecked") + StateMachine2.Transition transition = mock(StateMachine2.Transition.class); + when(vm.getId()).thenReturn(11L); + when(topologyGuru.getVpcIdsVmIsPartOf(11L)).thenReturn(List.of(VPC_ID, SECOND_VPC_ID)); + when(vpcDao.findById(VPC_ID)).thenReturn(firstVpc); + when(vpcDao.findById(SECOND_VPC_ID)).thenReturn(secondVpc); + when(firstVpc.usesDistributedRouter()).thenReturn(true); + when(secondVpc.usesDistributedRouter()).thenReturn(true); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(true); + when(vpcManager.isProviderSupportServiceInVpc(SECOND_VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(false); + when(transition.getCurrentState()).thenReturn(VirtualMachine.State.Starting); + when(transition.getEvent()).thenReturn(VirtualMachine.Event.OperationSucceeded); + when(transition.getToState()).thenReturn(VirtualMachine.State.Running); + Network malformedNetwork = mock(Network.class); + when(topologyGuru.getVpcSpannedHosts(VPC_ID)).thenReturn(Collections.emptyList()); + when(topologyGuru.getAllActiveVmsInVpc(VPC_ID)).thenReturn(Collections.emptyList()); + doReturn(List.of(malformedNetwork)).when(vpcManager).getVpcNetworks(VPC_ID); + when(firstVpc.getUuid()).thenReturn("vpc-uuid"); + when(firstVpc.getCidr()).thenReturn("10.0.0.0/16"); + when(malformedNetwork.getUuid()).thenReturn("network-uuid"); + when(malformedNetwork.getBroadcastDomainType()).thenReturn(BroadcastDomainType.NSX); + + assertTrue(manager.postStateTransitionEvent(transition, vm, true, null)); + + verify(vpcDao).findById(SECOND_VPC_ID); + } + + @Test + public void testPostStateTransitionEventBuildsTopologyForOvsVpc() { + VpcVO vpc = mock(VpcVO.class); + VMInstanceVO vm = mock(VMInstanceVO.class); + @SuppressWarnings("unchecked") + StateMachine2.Transition transition = mock(StateMachine2.Transition.class); + when(vm.getId()).thenReturn(11L); + when(topologyGuru.getVpcIdsVmIsPartOf(11L)).thenReturn(List.of(VPC_ID)); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.usesDistributedRouter()).thenReturn(true); + when(vpc.getUuid()).thenReturn("vpc-uuid"); + when(vpc.getCidr()).thenReturn("10.0.0.0/16"); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(true); + when(transition.getCurrentState()).thenReturn(VirtualMachine.State.Starting); + when(transition.getEvent()).thenReturn(VirtualMachine.Event.OperationSucceeded); + when(transition.getToState()).thenReturn(VirtualMachine.State.Running); + when(topologyGuru.getVpcSpannedHosts(VPC_ID)).thenReturn(Collections.emptyList()); + when(topologyGuru.getAllActiveVmsInVpc(VPC_ID)).thenReturn(Collections.emptyList()); + doReturn(Collections.emptyList()).when(vpcManager).getVpcNetworks(VPC_ID); + prepareSequenceNumber(VPC_ID); + + assertTrue(manager.postStateTransitionEvent(transition, vm, true, null)); + + verify(vpcManager).getVpcNetworks(VPC_ID); + verify(manager._vpcDrSeqNoDao).update(1L, sequenceNumber); + } + @Test public void testNetworkAclSubscriberIgnoresNsxDistributedVpc() { VpcVO vpc = mock(VpcVO.class); @@ -183,6 +247,28 @@ public void testNetworkAclSubscriberIgnoresNsxDistributedVpc() { verify(vpcManager, never()).getVpcNetworks(anyLong()); } + @Test + public void testNetworkAclSubscriberBuildsPolicyForOvsVpc() { + VpcVO vpc = mock(VpcVO.class); + NetworkVO network = mock(NetworkVO.class); + when(network.getVpcId()).thenReturn(VPC_ID); + when(vpcDao.findById(VPC_ID)).thenReturn(vpc); + when(vpc.usesDistributedRouter()).thenReturn(true); + when(vpc.getUuid()).thenReturn("vpc-uuid"); + when(vpc.getCidr()).thenReturn("10.0.0.0/16"); + when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs)) + .thenReturn(true); + doReturn(List.of(network)).when(vpcManager).getVpcNetworks(VPC_ID); + when(network.getNetworkACLId()).thenReturn(null); + when(topologyGuru.getVpcSpannedHosts(VPC_ID)).thenReturn(Collections.emptyList()); + prepareSequenceNumber(VPC_ID); + + manager.new NetworkAclEventsSubscriber().onPublishMessage("sender", "Network_ACL_Replaced", network); + + verify(vpcManager).getVpcNetworks(VPC_ID); + verify(manager._vpcDrSeqNoDao).update(1L, sequenceNumber); + } + @Test public void testPrepareVpcTopologyUpdateRejectsNonVswitchTier() { VpcVO vpc = mock(VpcVO.class); @@ -214,6 +300,34 @@ public void testPrepareVpcTopologyUpdateRejectsNonNumericGreKey() { assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); } + @Test + public void testPrepareVpcTopologyUpdateRejectsRepeatedDelimiterInBroadcastKey() { + prepareVswitchNetwork("7..123"); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + } + + @Test + public void testPrepareVpcTopologyUpdateRejectsLeadingDelimiterInBroadcastKey() { + prepareVswitchNetwork(".7.123"); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + } + + @Test + public void testPrepareVpcTopologyUpdateRejectsTrailingDelimiterInBroadcastKey() { + prepareVswitchNetwork("7.123."); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + } + + @Test + public void testPrepareVpcTopologyUpdateRejectsGreKeyOutsideIntegerRange() { + prepareVswitchNetwork("7.2147483648"); + + assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID)); + } + @Test public void testPrepareVpcTopologyUpdateRejectsMissingGatewayNic() { prepareVswitchNetwork("7.123"); @@ -253,4 +367,13 @@ private Network prepareVswitchNetwork(String broadcastKey) { when(network.getBroadcastUri()).thenReturn(BroadcastDomainType.Vswitch.toUri(broadcastKey)); return network; } + + private void prepareSequenceNumber(long vpcId) { + sequenceNumber = mock(VpcDistributedRouterSeqNoVO.class); + when(sequenceNumber.getId()).thenReturn(1L); + when(sequenceNumber.getTopologyUpdateSequenceNo()).thenReturn(1L); + when(sequenceNumber.getPolicyUpdateSequenceNo()).thenReturn(1L); + when(manager._vpcDrSeqNoDao.findByVpcId(vpcId)).thenReturn(sequenceNumber); + when(manager._vpcDrSeqNoDao.lockRow(1L, true)).thenReturn(sequenceNumber); + } }