Skip to content

Commit 6a89077

Browse files
committed
OVS: ignore distributed VPCs owned by other providers
1 parent 5328528 commit 6a89077

2 files changed

Lines changed: 290 additions & 18 deletions

File tree

plugins/network-elements/ovs/src/main/java/com/cloud/network/ovs/OvsTunnelManagerImpl.java

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,12 @@ boolean isVpcEnabledForDistributedRouter(long vpcId) {
396396
return vpc.usesDistributedRouter();
397397
}
398398

399+
boolean isOvsDistributedRouterVpc(long vpcId) {
400+
VpcVO vpc = _vpcDao.findById(vpcId);
401+
return vpc != null && vpc.usesDistributedRouter()
402+
&& _vpcMgr.isProviderSupportServiceInVpc(vpcId, Network.Service.Connectivity, Network.Provider.Ovs);
403+
}
404+
399405
@Override
400406
public void checkAndPrepareHostForTunnelNetwork(Network nw, Host host) {
401407
if (nw.getVpcId() != null && isVpcEnabledForDistributedRouter(nw.getVpcId())) {
@@ -684,10 +690,8 @@ private void handleVmStateChange(VMInstanceVO vm) {
684690
}
685691

686692
for (Long vpcId: vpcIds) {
687-
VpcVO vpc = _vpcDao.findById(vpcId);
688-
// nothing to do if the VPC is not setup for distributed routing
689-
if (vpc == null || !vpc.usesDistributedRouter()) {
690-
return;
693+
if (!isOvsDistributedRouterVpc(vpcId)) {
694+
continue;
691695
}
692696

693697
// 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) {
754758
}
755759

756760
for (Network network: vpcNetworks) {
761+
if (network.getBroadcastDomainType() != BroadcastDomainType.Vswitch || network.getBroadcastUri() == null) {
762+
throw new CloudRuntimeException(String.format(
763+
"OVS distributed-router VPC %s contains network %s without a Vswitch broadcast URI",
764+
vpc.getUuid(), network.getUuid()));
765+
}
757766
String key = network.getBroadcastUri().getAuthority();
758-
long gre_key;
759-
if (key.contains(".")) {
760-
String[] parts = key.split("\\.");
761-
gre_key = Long.parseLong(parts[1]);
762-
} else {
763-
try {
764-
gre_key = Long.parseLong(BroadcastDomainType.getValue(key));
765-
} catch (Exception e) {
766-
return null;
767-
}
767+
String[] parts = StringUtils.split(key, '.');
768+
if (parts == null || parts.length != 2 || !String.valueOf(vpcId).equals(parts[0])) {
769+
throw new CloudRuntimeException(String.format(
770+
"OVS distributed-router network %s has invalid broadcast key %s for VPC %s",
771+
network.getUuid(), key, vpc.getUuid()));
772+
}
773+
long greKey;
774+
try {
775+
greKey = Long.parseLong(parts[1]);
776+
} catch (NumberFormatException e) {
777+
throw new CloudRuntimeException(String.format(
778+
"OVS distributed-router network %s has non-numeric GRE key %s",
779+
network.getUuid(), parts[1]), e);
768780
}
769781
NicVO nic = _nicDao.findByIp4AddressAndNetworkId(network.getGateway(), network.getId());
770-
OvsVpcPhysicalTopologyConfigCommand.Tier tier = new OvsVpcPhysicalTopologyConfigCommand.Tier(gre_key,
782+
if (nic == null) {
783+
throw new CloudRuntimeException(String.format(
784+
"Unable to find the gateway NIC for OVS distributed-router network %s", network.getUuid()));
785+
}
786+
OvsVpcPhysicalTopologyConfigCommand.Tier tier = new OvsVpcPhysicalTopologyConfigCommand.Tier(greKey,
771787
network.getUuid(), network.getGateway(), nic.getMacAddress(), network.getCidr());
772788
tiers.add(tier);
773789
}
@@ -802,9 +818,9 @@ public class NetworkAclEventsSubscriber implements MessageSubscriber {
802818
public void onPublishMessage(String senderAddress, String subject, Object args) {
803819
try {
804820
NetworkVO network = (NetworkVO) args;
805-
String bridgeName=generateBridgeNameForVpc(network.getVpcId());
806-
if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) {
807-
long vpcId = network.getVpcId();
821+
Long vpcId = network.getVpcId();
822+
if (vpcId != null && isOvsDistributedRouterVpc(vpcId)) {
823+
String bridgeName = generateBridgeNameForVpc(vpcId);
808824
OvsVpcRoutingPolicyConfigCommand cmd = prepareVpcRoutingPolicyUpdate(vpcId);
809825
cmd.setSequenceNumber(getNextRoutingPolicyUpdateSequenceNumber(vpcId));
810826

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package com.cloud.network.ovs;
19+
20+
import static org.junit.Assert.assertFalse;
21+
import static org.junit.Assert.assertThrows;
22+
import static org.junit.Assert.assertTrue;
23+
import static org.mockito.ArgumentMatchers.anyLong;
24+
import static org.mockito.Mockito.doReturn;
25+
import static org.mockito.Mockito.mock;
26+
import static org.mockito.Mockito.never;
27+
import static org.mockito.Mockito.verify;
28+
import static org.mockito.Mockito.when;
29+
30+
import java.util.Collections;
31+
import java.util.List;
32+
33+
import org.junit.Before;
34+
import org.junit.Test;
35+
36+
import com.cloud.agent.AgentManager;
37+
import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand;
38+
import com.cloud.host.dao.HostDao;
39+
import com.cloud.network.Network;
40+
import com.cloud.network.Networks.BroadcastDomainType;
41+
import com.cloud.network.dao.NetworkDao;
42+
import com.cloud.network.dao.NetworkVO;
43+
import com.cloud.network.ovs.dao.VpcDistributedRouterSeqNoDao;
44+
import com.cloud.network.vpc.VpcManager;
45+
import com.cloud.network.vpc.VpcVO;
46+
import com.cloud.network.vpc.dao.VpcDao;
47+
import com.cloud.utils.exception.CloudRuntimeException;
48+
import com.cloud.utils.fsm.StateMachine2;
49+
import com.cloud.vm.NicVO;
50+
import com.cloud.vm.VMInstanceVO;
51+
import com.cloud.vm.VirtualMachine;
52+
import com.cloud.vm.dao.NicDao;
53+
import com.cloud.vm.dao.VMInstanceDao;
54+
55+
public class OvsTunnelManagerImplTest {
56+
private static final long VPC_ID = 7L;
57+
private static final long SECOND_VPC_ID = 8L;
58+
59+
private OvsTunnelManagerImpl manager;
60+
private VpcDao vpcDao;
61+
private VpcManager vpcManager;
62+
private OvsNetworkTopologyGuru topologyGuru;
63+
private NicDao nicDao;
64+
65+
@Before
66+
public void setUp() {
67+
manager = new OvsTunnelManagerImpl();
68+
vpcDao = mock(VpcDao.class);
69+
vpcManager = mock(VpcManager.class);
70+
topologyGuru = mock(OvsNetworkTopologyGuru.class);
71+
nicDao = mock(NicDao.class);
72+
manager._vpcDao = vpcDao;
73+
manager._vpcMgr = vpcManager;
74+
manager._ovsNetworkToplogyGuru = topologyGuru;
75+
manager._nicDao = nicDao;
76+
manager._hostDao = mock(HostDao.class);
77+
manager._vmInstanceDao = mock(VMInstanceDao.class);
78+
manager._networkDao = mock(NetworkDao.class);
79+
manager._vpcDrSeqNoDao = mock(VpcDistributedRouterSeqNoDao.class);
80+
manager._agentMgr = mock(AgentManager.class);
81+
}
82+
83+
@Test
84+
public void testIsOvsDistributedRouterVpcReturnsFalseWhenVpcIsMissing() {
85+
assertFalse(manager.isOvsDistributedRouterVpc(VPC_ID));
86+
verify(vpcManager, never()).isProviderSupportServiceInVpc(anyLong(),
87+
org.mockito.ArgumentMatchers.any(Network.Service.class),
88+
org.mockito.ArgumentMatchers.any(Network.Provider.class));
89+
}
90+
91+
@Test
92+
public void testIsOvsDistributedRouterVpcReturnsFalseWhenVpcIsNotDistributed() {
93+
VpcVO vpc = mock(VpcVO.class);
94+
when(vpcDao.findById(VPC_ID)).thenReturn(vpc);
95+
when(vpc.usesDistributedRouter()).thenReturn(false);
96+
97+
assertFalse(manager.isOvsDistributedRouterVpc(VPC_ID));
98+
}
99+
100+
@Test
101+
public void testIsOvsDistributedRouterVpcReturnsFalseForNsxDistributedVpc() {
102+
VpcVO vpc = mock(VpcVO.class);
103+
when(vpcDao.findById(VPC_ID)).thenReturn(vpc);
104+
when(vpc.usesDistributedRouter()).thenReturn(true);
105+
when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs))
106+
.thenReturn(false);
107+
108+
assertFalse(manager.isOvsDistributedRouterVpc(VPC_ID));
109+
}
110+
111+
@Test
112+
public void testIsOvsDistributedRouterVpcReturnsTrueForOvsConnectivityDistributedVpc() {
113+
VpcVO vpc = mock(VpcVO.class);
114+
when(vpcDao.findById(VPC_ID)).thenReturn(vpc);
115+
when(vpc.usesDistributedRouter()).thenReturn(true);
116+
when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs))
117+
.thenReturn(true);
118+
119+
assertTrue(manager.isOvsDistributedRouterVpc(VPC_ID));
120+
}
121+
122+
@Test
123+
public void testPostStateTransitionEventIgnoresNsxDistributedVpc() {
124+
VpcVO vpc = mock(VpcVO.class);
125+
VMInstanceVO vm = mock(VMInstanceVO.class);
126+
@SuppressWarnings("unchecked")
127+
StateMachine2.Transition<VirtualMachine.State, VirtualMachine.Event> transition = mock(StateMachine2.Transition.class);
128+
when(vm.getId()).thenReturn(11L);
129+
when(topologyGuru.getVpcIdsVmIsPartOf(11L)).thenReturn(List.of(VPC_ID));
130+
when(vpcDao.findById(VPC_ID)).thenReturn(vpc);
131+
when(vpc.usesDistributedRouter()).thenReturn(true);
132+
when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs))
133+
.thenReturn(false);
134+
when(transition.getCurrentState()).thenReturn(VirtualMachine.State.Starting);
135+
when(transition.getEvent()).thenReturn(VirtualMachine.Event.OperationSucceeded);
136+
when(transition.getToState()).thenReturn(VirtualMachine.State.Running);
137+
138+
assertTrue(manager.postStateTransitionEvent(transition, vm, true, null));
139+
140+
verify(topologyGuru, never()).getVpcSpannedHosts(anyLong());
141+
verify(vpcManager, never()).getVpcNetworks(anyLong());
142+
}
143+
144+
@Test
145+
public void testPostStateTransitionEventContinuesAfterNonOvsVpc() {
146+
VpcVO firstVpc = mock(VpcVO.class);
147+
VpcVO secondVpc = mock(VpcVO.class);
148+
VMInstanceVO vm = mock(VMInstanceVO.class);
149+
@SuppressWarnings("unchecked")
150+
StateMachine2.Transition<VirtualMachine.State, VirtualMachine.Event> transition = mock(StateMachine2.Transition.class);
151+
when(vm.getId()).thenReturn(11L);
152+
when(topologyGuru.getVpcIdsVmIsPartOf(11L)).thenReturn(List.of(VPC_ID, SECOND_VPC_ID));
153+
when(vpcDao.findById(VPC_ID)).thenReturn(firstVpc);
154+
when(vpcDao.findById(SECOND_VPC_ID)).thenReturn(secondVpc);
155+
when(firstVpc.usesDistributedRouter()).thenReturn(true);
156+
when(secondVpc.usesDistributedRouter()).thenReturn(true);
157+
when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs))
158+
.thenReturn(false);
159+
when(vpcManager.isProviderSupportServiceInVpc(SECOND_VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs))
160+
.thenReturn(false);
161+
when(transition.getCurrentState()).thenReturn(VirtualMachine.State.Starting);
162+
when(transition.getEvent()).thenReturn(VirtualMachine.Event.OperationSucceeded);
163+
when(transition.getToState()).thenReturn(VirtualMachine.State.Running);
164+
165+
assertTrue(manager.postStateTransitionEvent(transition, vm, true, null));
166+
167+
verify(vpcDao).findById(SECOND_VPC_ID);
168+
}
169+
170+
@Test
171+
public void testNetworkAclSubscriberIgnoresNsxDistributedVpc() {
172+
VpcVO vpc = mock(VpcVO.class);
173+
NetworkVO network = mock(NetworkVO.class);
174+
when(network.getVpcId()).thenReturn(VPC_ID);
175+
when(vpcDao.findById(VPC_ID)).thenReturn(vpc);
176+
when(vpc.usesDistributedRouter()).thenReturn(true);
177+
when(vpcManager.isProviderSupportServiceInVpc(VPC_ID, Network.Service.Connectivity, Network.Provider.Ovs))
178+
.thenReturn(false);
179+
180+
manager.new NetworkAclEventsSubscriber().onPublishMessage("sender", "Network_ACL_Replaced", network);
181+
182+
verify(topologyGuru, never()).getVpcSpannedHosts(anyLong());
183+
verify(vpcManager, never()).getVpcNetworks(anyLong());
184+
}
185+
186+
@Test
187+
public void testPrepareVpcTopologyUpdateRejectsNonVswitchTier() {
188+
VpcVO vpc = mock(VpcVO.class);
189+
Network network = mock(Network.class);
190+
when(vpcDao.findById(VPC_ID)).thenReturn(vpc);
191+
when(vpc.getUuid()).thenReturn("vpc-uuid");
192+
doReturn(List.of(network)).when(vpcManager).getVpcNetworks(VPC_ID);
193+
when(topologyGuru.getVpcSpannedHosts(VPC_ID)).thenReturn(Collections.emptyList());
194+
when(topologyGuru.getAllActiveVmsInVpc(VPC_ID)).thenReturn(Collections.emptyList());
195+
when(network.getUuid()).thenReturn("network-uuid");
196+
when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.NSX);
197+
198+
assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID));
199+
}
200+
201+
@Test
202+
public void testPrepareVpcTopologyUpdateRejectsBroadcastKeyForAnotherVpc() {
203+
Network network = prepareVswitchNetwork("8.123");
204+
205+
assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID));
206+
207+
verify(nicDao, never()).findByIp4AddressAndNetworkId("10.0.1.1", 13L);
208+
}
209+
210+
@Test
211+
public void testPrepareVpcTopologyUpdateRejectsNonNumericGreKey() {
212+
prepareVswitchNetwork("7.invalid");
213+
214+
assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID));
215+
}
216+
217+
@Test
218+
public void testPrepareVpcTopologyUpdateRejectsMissingGatewayNic() {
219+
prepareVswitchNetwork("7.123");
220+
221+
assertThrows(CloudRuntimeException.class, () -> manager.prepareVpcTopologyUpdate(VPC_ID));
222+
}
223+
224+
@Test
225+
public void testPrepareVpcTopologyUpdateBuildsValidOvsTopology() {
226+
prepareVswitchNetwork("7.123");
227+
NicVO gatewayNic = mock(NicVO.class);
228+
when(nicDao.findByIp4AddressAndNetworkId("10.0.1.1", 13L)).thenReturn(gatewayNic);
229+
when(gatewayNic.getMacAddress()).thenReturn("02:00:00:00:00:01");
230+
231+
OvsVpcPhysicalTopologyConfigCommand command = manager.prepareVpcTopologyUpdate(VPC_ID);
232+
233+
String topology = command.getVpcConfigInJson();
234+
assertTrue(topology.contains("\"grekey\":123"));
235+
assertTrue(topology.contains("\"networkuuid\":\"network-uuid\""));
236+
assertTrue(topology.contains("\"gatewaymac\":\"02:00:00:00:00:01\""));
237+
}
238+
239+
private Network prepareVswitchNetwork(String broadcastKey) {
240+
VpcVO vpc = mock(VpcVO.class);
241+
Network network = mock(Network.class);
242+
when(vpcDao.findById(VPC_ID)).thenReturn(vpc);
243+
when(vpc.getUuid()).thenReturn("vpc-uuid");
244+
when(vpc.getCidr()).thenReturn("10.0.0.0/16");
245+
doReturn(List.of(network)).when(vpcManager).getVpcNetworks(VPC_ID);
246+
when(topologyGuru.getVpcSpannedHosts(VPC_ID)).thenReturn(Collections.emptyList());
247+
when(topologyGuru.getAllActiveVmsInVpc(VPC_ID)).thenReturn(Collections.emptyList());
248+
when(network.getId()).thenReturn(13L);
249+
when(network.getUuid()).thenReturn("network-uuid");
250+
when(network.getGateway()).thenReturn("10.0.1.1");
251+
when(network.getCidr()).thenReturn("10.0.1.0/24");
252+
when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.Vswitch);
253+
when(network.getBroadcastUri()).thenReturn(BroadcastDomainType.Vswitch.toUri(broadcastKey));
254+
return network;
255+
}
256+
}

0 commit comments

Comments
 (0)