Skip to content

Commit 9f079d8

Browse files
committed
fix: LB Creation avoid 404 API errors due to non-needed patches
Fix existing lbVirtualServer search by lbVirtualServerName
1 parent 93239e0 commit 9f079d8

File tree

2 files changed

+238
-15
lines changed

2 files changed

+238
-15
lines changed

plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import com.vmware.vapi.internal.protocol.client.rest.authn.BasicAuthenticationAppender;
8484
import com.vmware.vapi.protocol.HttpConfiguration;
8585
import com.vmware.vapi.std.errors.Error;
86+
import com.vmware.vapi.std.errors.NotFound;
8687
import org.apache.cloudstack.resource.NsxLoadBalancerMember;
8788
import org.apache.cloudstack.resource.NsxNetworkRule;
8889
import org.apache.cloudstack.utils.NsxControllerUtils;
@@ -96,9 +97,11 @@
9697
import java.util.Locale;
9798
import java.util.Objects;
9899
import java.util.Optional;
100+
import java.util.Set;
99101
import java.util.function.Function;
100102
import java.util.stream.Collectors;
101103

104+
import static java.util.stream.Collectors.toSet;
102105
import static org.apache.cloudstack.utils.NsxControllerUtils.getServerPoolMemberName;
103106
import static org.apache.cloudstack.utils.NsxControllerUtils.getServerPoolName;
104107
import static org.apache.cloudstack.utils.NsxControllerUtils.getServiceName;
@@ -659,6 +662,13 @@ public void createNsxLbServerPool(List<NsxLoadBalancerMember> memberList, String
659662
String activeMonitorPath = getLbActiveMonitorPath(lbServerPoolName, privatePort, protocol);
660663
List<LBPoolMember> members = getLbPoolMembers(memberList, tier1GatewayName);
661664
LbPools lbPools = (LbPools) nsxService.apply(LbPools.class);
665+
List<LBPoolMember> existingMembers = getNsxLbServerPool(lbPools, lbServerPoolName)
666+
.map(LBPool::getMembers)
667+
.orElse(null);
668+
// Skip if pool exists and members unchanged
669+
if (existingMembers != null && hasSamePoolMembers(existingMembers, members)) {
670+
return;
671+
}
662672
LBPool lbPool = new LBPool.Builder()
663673
.setId(lbServerPoolName)
664674
.setDisplayName(lbServerPoolName)
@@ -676,27 +686,65 @@ public void createNsxLbServerPool(List<NsxLoadBalancerMember> memberList, String
676686
}
677687
}
678688

689+
private Optional<LBPool> getNsxLbServerPool(LbPools lbPools, String lbServerPoolName) {
690+
try {
691+
return Optional.ofNullable(lbPools.get(lbServerPoolName));
692+
} catch (NotFound e) {
693+
logger.debug("Server Pool not found: {}", lbServerPoolName);
694+
return Optional.empty();
695+
}
696+
}
697+
698+
private boolean hasSamePoolMembers(List<LBPoolMember> poolMembersCurrent, List<LBPoolMember> poolMembersUpdate) {
699+
if (poolMembersCurrent == null || poolMembersUpdate == null) {
700+
return poolMembersCurrent == poolMembersUpdate;
701+
}
702+
if (poolMembersCurrent.size() != poolMembersUpdate.size()) {
703+
return false;
704+
}
705+
Set<String> currentMembersSet = poolMembersCurrent.stream()
706+
.map(this::buildPoolMemberKey)
707+
.collect(toSet());
708+
return poolMembersUpdate.stream()
709+
.map(this::buildPoolMemberKey)
710+
.allMatch(currentMembersSet::contains);
711+
}
712+
713+
private String buildPoolMemberKey(LBPoolMember member) {
714+
return member.getIpAddress() + ':' + member.getPort() + ':' + member.getDisplayName();
715+
}
716+
679717
private String getLbActiveMonitorPath(String lbServerPoolName, String port, String protocol) {
680718
LbMonitorProfiles lbActiveMonitor = (LbMonitorProfiles) nsxService.apply(LbMonitorProfiles.class);
681719
String lbMonitorProfileId = getActiveMonitorProfileName(lbServerPoolName, port, protocol);
682-
if ("TCP".equals(protocol.toUpperCase(Locale.ROOT))) {
683-
LBTcpMonitorProfile lbTcpMonitorProfile = new LBTcpMonitorProfile.Builder(TCP_MONITOR_PROFILE)
684-
.setDisplayName(lbMonitorProfileId)
685-
.setMonitorPort(Long.parseLong(port))
686-
.build();
687-
lbActiveMonitor.patch(lbMonitorProfileId, lbTcpMonitorProfile);
688-
} else if ("UDP".equals(protocol.toUpperCase(Locale.ROOT))) {
689-
LBIcmpMonitorProfile icmpMonitorProfile = new LBIcmpMonitorProfile.Builder(ICMP_MONITOR_PROFILE)
690-
.setDisplayName(lbMonitorProfileId)
691-
.build();
692-
lbActiveMonitor.patch(lbMonitorProfileId, icmpMonitorProfile);
720+
Optional<Structure> monitorProfile = getMonitorProfile(lbActiveMonitor, lbMonitorProfileId);
721+
if (monitorProfile.isEmpty()) {
722+
if ("TCP".equals(protocol.toUpperCase(Locale.ROOT))) {
723+
LBTcpMonitorProfile lbTcpMonitorProfile = new LBTcpMonitorProfile.Builder(TCP_MONITOR_PROFILE)
724+
.setDisplayName(lbMonitorProfileId)
725+
.setMonitorPort(Long.parseLong(port))
726+
.build();
727+
lbActiveMonitor.patch(lbMonitorProfileId, lbTcpMonitorProfile);
728+
} else if ("UDP".equals(protocol.toUpperCase(Locale.ROOT))) {
729+
LBIcmpMonitorProfile icmpMonitorProfile = new LBIcmpMonitorProfile.Builder(ICMP_MONITOR_PROFILE)
730+
.setDisplayName(lbMonitorProfileId)
731+
.build();
732+
lbActiveMonitor.patch(lbMonitorProfileId, icmpMonitorProfile);
733+
}
734+
monitorProfile = getMonitorProfile(lbActiveMonitor, lbMonitorProfileId);
693735
}
694-
695-
LBMonitorProfileListResult listResult = listLBActiveMonitors(lbActiveMonitor);
696-
Optional<Structure> monitorProfile = listResult.getResults().stream().filter(profile -> profile._getDataValue().getField("id").toString().equals(lbMonitorProfileId)).findFirst();
697736
return monitorProfile.map(structure -> structure._getDataValue().getField("path").toString()).orElse(null);
698737
}
699738

739+
private Optional<Structure> getMonitorProfile(LbMonitorProfiles lbActiveMonitor, String lbMonitorProfileId) {
740+
try {
741+
return Optional.ofNullable(lbActiveMonitor.get(lbMonitorProfileId));
742+
} catch (NotFound e) {
743+
logger.debug("LB Monitor Profile not found: {}", lbMonitorProfileId);
744+
return Optional.empty();
745+
}
746+
}
747+
700748
LBMonitorProfileListResult listLBActiveMonitors(LbMonitorProfiles lbActiveMonitor) {
701749
return lbActiveMonitor.list(null, false, null, null, null, null);
702750
}
@@ -735,7 +783,7 @@ public void createAndAddNsxLbVirtualServer(String tier1GatewayName, long lbId, S
735783
String lbVirtualServerName = getVirtualServerName(tier1GatewayName, lbId);
736784
String lbServiceName = getLoadBalancerName(tier1GatewayName);
737785
LbVirtualServers lbVirtualServers = (LbVirtualServers) nsxService.apply(LbVirtualServers.class);
738-
if (Objects.nonNull(getLbVirtualServerService(lbVirtualServers, lbServiceName))) {
786+
if (Objects.nonNull(getLbVirtualServerService(lbVirtualServers, lbVirtualServerName))) {
739787
return;
740788
}
741789
LBVirtualServer lbVirtualServer = new LBVirtualServer.Builder()

plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,22 @@
2121
import com.vmware.nsx.cluster.Status;
2222
import com.vmware.nsx.model.ClusterStatus;
2323
import com.vmware.nsx.model.ControllerClusterStatus;
24+
import com.vmware.nsx_policy.infra.LbMonitorProfiles;
25+
import com.vmware.nsx_policy.infra.LbPools;
26+
import com.vmware.nsx_policy.infra.LbServices;
27+
import com.vmware.nsx_policy.infra.LbVirtualServers;
2428
import com.vmware.nsx_policy.infra.domains.Groups;
2529
import com.vmware.nsx_policy.model.Group;
30+
import com.vmware.nsx_policy.model.LBTcpMonitorProfile;
31+
import com.vmware.nsx_policy.model.LBPool;
32+
import com.vmware.nsx_policy.model.LBPoolMember;
33+
import com.vmware.nsx_policy.model.LBVirtualServer;
2634
import com.vmware.nsx_policy.model.PathExpression;
2735
import com.vmware.vapi.bindings.Service;
36+
import com.vmware.vapi.bindings.Structure;
37+
import com.vmware.vapi.std.errors.NotFound;
38+
import org.apache.cloudstack.resource.NsxLoadBalancerMember;
39+
import org.apache.cloudstack.utils.NsxControllerUtils;
2840
import org.junit.Assert;
2941
import org.junit.Before;
3042
import org.junit.Test;
@@ -36,8 +48,16 @@
3648
import java.util.List;
3749
import java.util.function.Function;
3850

51+
import static org.mockito.ArgumentMatchers.any;
52+
import static org.mockito.ArgumentMatchers.anyString;
53+
import static org.mockito.ArgumentMatchers.eq;
54+
import static org.mockito.Mockito.never;
55+
import static org.mockito.Mockito.verify;
56+
3957
public class NsxApiClientTest {
4058

59+
private static final String TIER_1_GATEWAY_NAME = "t1";
60+
4161
@Mock
4262
private Function<Class<? extends Service>, Service> nsxService;
4363
@Mock
@@ -108,4 +128,159 @@ public void testIsNsxControllerActive() {
108128
Mockito.when(clusterStatus.getControlClusterStatus()).thenReturn(status);
109129
Assert.assertTrue(client.isNsxControllerActive());
110130
}
131+
132+
@Test
133+
public void testCreateNsxLbServerPoolExistingMonitorProfileSkipsMonitorPatch() {
134+
String lbServerPoolName = NsxControllerUtils.getServerPoolName(TIER_1_GATEWAY_NAME, 1L);
135+
List<NsxLoadBalancerMember> memberList = List.of(new NsxLoadBalancerMember(1L, "10.0.0.1", 80));
136+
137+
LbPools lbPools = Mockito.mock(LbPools.class);
138+
LbMonitorProfiles lbMonitorProfiles = mockLbMonitorProfiles();
139+
140+
Mockito.when(nsxService.apply(LbPools.class)).thenReturn(lbPools);
141+
Mockito.when(lbPools.get(lbServerPoolName)).thenThrow(new NotFound(null, null));
142+
143+
client.createNsxLbServerPool(memberList, TIER_1_GATEWAY_NAME, lbServerPoolName, "roundrobin", "80", "TCP");
144+
145+
verify(lbMonitorProfiles, never()).patch(anyString(), any(LBTcpMonitorProfile.class));
146+
verify(lbPools).patch(eq(lbServerPoolName), any(LBPool.class));
147+
}
148+
149+
@Test
150+
public void testCreateNsxLbServerPoolMissingMonitorProfilePerformsPatch() {
151+
String lbServerPoolName = NsxControllerUtils.getServerPoolName(TIER_1_GATEWAY_NAME, 1L);
152+
List<NsxLoadBalancerMember> memberList = List.of(new NsxLoadBalancerMember(1L, "10.0.0.1", 80));
153+
154+
LbPools lbPools = Mockito.mock(LbPools.class);
155+
LbMonitorProfiles lbMonitorProfiles = Mockito.mock(LbMonitorProfiles.class);
156+
Structure monitorStructure = Mockito.mock(Structure.class, Mockito.RETURNS_DEEP_STUBS);
157+
158+
Mockito.when(nsxService.apply(LbPools.class)).thenReturn(lbPools);
159+
Mockito.when(nsxService.apply(LbMonitorProfiles.class)).thenReturn(lbMonitorProfiles);
160+
Mockito.when(lbMonitorProfiles.get(anyString())).thenThrow(new NotFound(null, null)).thenReturn(monitorStructure);
161+
Mockito.when(monitorStructure._getDataValue().getField("path").toString()).thenReturn("/infra/lb-monitor-profiles/test");
162+
Mockito.when(lbPools.get(lbServerPoolName)).thenThrow(new NotFound(null, null));
163+
164+
client.createNsxLbServerPool(memberList, TIER_1_GATEWAY_NAME, lbServerPoolName, "roundrobin", "80", "TCP");
165+
166+
verify(lbMonitorProfiles).patch(anyString(), any(LBTcpMonitorProfile.class));
167+
verify(lbPools).patch(eq(lbServerPoolName), any(LBPool.class));
168+
}
169+
170+
@Test
171+
public void testCreateNsxLbServerPoolPoolExistsWithSameMembersSkipsPatch() {
172+
long lbId = 1L;
173+
String lbServerPoolName = NsxControllerUtils.getServerPoolName(TIER_1_GATEWAY_NAME, lbId);
174+
List<NsxLoadBalancerMember> memberList = List.of(
175+
new NsxLoadBalancerMember(1L, "10.0.0.1", 80),
176+
new NsxLoadBalancerMember(2L, "10.0.0.2", 80)
177+
);
178+
List<LBPoolMember> sameMembers = List.of(
179+
new LBPoolMember.Builder()
180+
.setDisplayName(NsxControllerUtils.getServerPoolMemberName(TIER_1_GATEWAY_NAME, 2L))
181+
.setIpAddress("10.0.0.2")
182+
.setPort("80")
183+
.build(),
184+
new LBPoolMember.Builder()
185+
.setDisplayName(NsxControllerUtils.getServerPoolMemberName(TIER_1_GATEWAY_NAME, 1L))
186+
.setIpAddress("10.0.0.1")
187+
.setPort("80")
188+
.build()
189+
);
190+
191+
LbPools lbPools = Mockito.mock(LbPools.class);
192+
LBPool existingPool = Mockito.mock(LBPool.class);
193+
LbMonitorProfiles lbMonitorProfiles = mockLbMonitorProfiles();
194+
195+
Mockito.when(nsxService.apply(LbPools.class)).thenReturn(lbPools);
196+
Mockito.when(lbPools.get(lbServerPoolName)).thenReturn(existingPool);
197+
Mockito.when(existingPool.getMembers()).thenReturn(sameMembers);
198+
199+
client.createNsxLbServerPool(memberList, TIER_1_GATEWAY_NAME, lbServerPoolName, "roundrobin", "80", "TCP");
200+
201+
verify(lbMonitorProfiles, never()).patch(anyString(), any(LBTcpMonitorProfile.class));
202+
verify(lbPools, never()).patch(anyString(), any(LBPool.class));
203+
}
204+
205+
@Test
206+
public void testCreateNsxLbServerPoolPoolExistsWithDifferentMembersPerformsPatch() {
207+
long lbId = 1L;
208+
String lbServerPoolName = NsxControllerUtils.getServerPoolName(TIER_1_GATEWAY_NAME, lbId);
209+
List<NsxLoadBalancerMember> memberList = List.of(
210+
new NsxLoadBalancerMember(1L, "10.0.0.1", 80),
211+
new NsxLoadBalancerMember(2L, "10.0.0.2", 80)
212+
);
213+
214+
LbPools lbPools = Mockito.mock(LbPools.class);
215+
LBPool existingPool = Mockito.mock(LBPool.class);
216+
217+
mockLbMonitorProfiles();
218+
Mockito.when(nsxService.apply(LbPools.class)).thenReturn(lbPools);
219+
Mockito.when(lbPools.get(lbServerPoolName)).thenReturn(existingPool);
220+
Mockito.when(existingPool.getMembers()).thenReturn(List.of(
221+
new LBPoolMember.Builder()
222+
.setDisplayName(NsxControllerUtils.getServerPoolMemberName(TIER_1_GATEWAY_NAME, 1L))
223+
.setIpAddress("10.0.0.10")
224+
.setPort("80")
225+
.build()
226+
));
227+
228+
client.createNsxLbServerPool(memberList, TIER_1_GATEWAY_NAME, lbServerPoolName, "roundrobin", "80", "TCP");
229+
230+
verify(lbPools).patch(eq(lbServerPoolName), any(LBPool.class));
231+
}
232+
233+
@Test
234+
public void testCreateNsxLbServerPoolPoolDoesNotExistPerformsPatch() {
235+
String lbServerPoolName = NsxControllerUtils.getServerPoolName(TIER_1_GATEWAY_NAME, 1L);
236+
List<NsxLoadBalancerMember> memberList = List.of(new NsxLoadBalancerMember(1L, "10.0.0.1", 80));
237+
238+
LbPools lbPools = Mockito.mock(LbPools.class);
239+
240+
mockLbMonitorProfiles();
241+
Mockito.when(nsxService.apply(LbPools.class)).thenReturn(lbPools);
242+
Mockito.when(lbPools.get(lbServerPoolName)).thenThrow(new NotFound(null, null));
243+
244+
client.createNsxLbServerPool(memberList, TIER_1_GATEWAY_NAME, lbServerPoolName, "roundrobin", "80", "TCP");
245+
246+
verify(lbPools).patch(eq(lbServerPoolName), any(LBPool.class));
247+
}
248+
249+
@Test
250+
public void testCreateAndAddNsxLbVirtualServerVirtualServerAlreadyExistsSkipsPatch() {
251+
long lbId = 1L;
252+
String lbVirtualServerName = NsxControllerUtils.getVirtualServerName(TIER_1_GATEWAY_NAME, lbId);
253+
String lbServiceName = NsxControllerUtils.getLoadBalancerName(TIER_1_GATEWAY_NAME);
254+
List<NsxLoadBalancerMember> memberList = List.of(new NsxLoadBalancerMember(1L, "10.0.0.1", 80));
255+
256+
LbPools lbPools = Mockito.mock(LbPools.class);
257+
LbServices lbServices = Mockito.mock(LbServices.class);
258+
LbVirtualServers lbVirtualServers = Mockito.mock(LbVirtualServers.class);
259+
LBVirtualServer existingVs = Mockito.mock(LBVirtualServer.class);
260+
261+
mockLbMonitorProfiles();
262+
Mockito.when(nsxService.apply(LbPools.class)).thenReturn(lbPools);
263+
Mockito.when(nsxService.apply(LbServices.class)).thenReturn(lbServices);
264+
Mockito.when(nsxService.apply(LbVirtualServers.class)).thenReturn(lbVirtualServers);
265+
Mockito.when(lbPools.get(anyString())).thenThrow(new NotFound(null, null));
266+
Mockito.when(lbServices.get(anyString())).thenReturn(null);
267+
Mockito.when(lbVirtualServers.get(lbVirtualServerName)).thenReturn(existingVs);
268+
269+
client.createAndAddNsxLbVirtualServer(TIER_1_GATEWAY_NAME, lbId, "192.168.1.1", "443",
270+
memberList, "roundrobin", "TCP", "80");
271+
272+
verify(lbVirtualServers).get(lbVirtualServerName);
273+
verify(lbVirtualServers, never()).get(lbServiceName);
274+
verify(lbVirtualServers, never()).patch(anyString(), any(LBVirtualServer.class));
275+
}
276+
277+
private LbMonitorProfiles mockLbMonitorProfiles() {
278+
LbMonitorProfiles lbMonitorProfiles = Mockito.mock(LbMonitorProfiles.class);
279+
Structure monitorStructure = Mockito.mock(Structure.class, Mockito.RETURNS_DEEP_STUBS);
280+
281+
Mockito.when(nsxService.apply(LbMonitorProfiles.class)).thenReturn(lbMonitorProfiles);
282+
Mockito.when(lbMonitorProfiles.get(anyString())).thenReturn(monitorStructure);
283+
Mockito.when(monitorStructure._getDataValue().getField("path").toString()).thenReturn("/infra/lb-monitor-profiles/test");
284+
return lbMonitorProfiles;
285+
}
111286
}

0 commit comments

Comments
 (0)