Skip to content

Commit df5158c

Browse files
committed
extensions: ignore removed offerings when removing a network service
1 parent f3cb957 commit df5158c

2 files changed

Lines changed: 59 additions & 4 deletions

File tree

‎framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImpl.java‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,10 @@
140140
import com.cloud.network.dao.PhysicalNetworkDao;
141141
import com.cloud.network.dao.PhysicalNetworkVO;
142142
import com.cloud.network.vpc.Vpc;
143+
import com.cloud.network.vpc.dao.VpcOfferingDao;
143144
import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
144145
import com.cloud.network.vpc.dao.VpcServiceMapDao;
146+
import com.cloud.offerings.dao.NetworkOfferingDao;
145147
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
146148
import com.cloud.org.Cluster;
147149
import com.cloud.serializer.GsonHelper;
@@ -248,6 +250,12 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
248250
@Inject
249251
VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
250252

253+
@Inject
254+
NetworkOfferingDao networkOfferingDao;
255+
256+
@Inject
257+
VpcOfferingDao vpcOfferingDao;
258+
251259
@Inject
252260
NetworkModel networkModel;
253261

@@ -1306,10 +1314,13 @@ protected void updateNetworkExtensionServicesOnPhysicalNetworks(ExtensionVO exte
13061314
return;
13071315
}
13081316
for (Service removedService : removedServices) {
1309-
boolean usedByNetworkOffering = CollectionUtils.isNotEmpty(
1310-
networkOfferingServiceMapDao.listOfferingIdsByServiceAndProvider(removedService, extension.getName()));
1311-
boolean usedByVpcOffering = CollectionUtils.isNotEmpty(
1312-
vpcOfferingServiceMapDao.listOfferingIdsByServiceAndProvider(removedService, extension.getName()));
1317+
// offering service map rows are kept when an offering is deleted, so ignore removed offerings
1318+
boolean usedByNetworkOffering = networkOfferingServiceMapDao
1319+
.listOfferingIdsByServiceAndProvider(removedService, extension.getName()).stream()
1320+
.anyMatch(offeringId -> networkOfferingDao.findById(offeringId) != null);
1321+
boolean usedByVpcOffering = vpcOfferingServiceMapDao
1322+
.listOfferingIdsByServiceAndProvider(removedService, extension.getName()).stream()
1323+
.anyMatch(offeringId -> vpcOfferingDao.findById(offeringId) != null);
13131324
if (usedByNetworkOffering || usedByVpcOffering) {
13141325
throw new CloudRuntimeException(String.format(
13151326
"Cannot remove service %s from extension '%s' as it is used by a network/VPC offering",

‎framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImplTest.java‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import java.util.HashMap;
5353
import java.util.List;
5454
import java.util.Map;
55+
import java.util.Set;
5556
import java.util.UUID;
5657

5758
import org.apache.cloudstack.acl.Role;
@@ -139,8 +140,12 @@
139140
import com.cloud.network.dao.PhysicalNetworkVO;
140141
import com.cloud.network.element.NetworkElement;
141142
import com.cloud.network.vpc.Vpc;
143+
import com.cloud.network.vpc.VpcOfferingVO;
144+
import com.cloud.network.vpc.dao.VpcOfferingDao;
142145
import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
143146
import com.cloud.network.vpc.dao.VpcServiceMapDao;
147+
import com.cloud.offerings.NetworkOfferingVO;
148+
import com.cloud.offerings.dao.NetworkOfferingDao;
144149
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
145150
import org.apache.cloudstack.extension.NetworkCustomActionProvider;
146151
import com.cloud.org.Cluster;
@@ -223,6 +228,12 @@ public class ExtensionsManagerImplTest {
223228
@Mock
224229
private VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
225230

231+
@Mock
232+
private NetworkOfferingDao networkOfferingDao;
233+
234+
@Mock
235+
private VpcOfferingDao vpcOfferingDao;
236+
226237
@Before
227238
public void setUp() {
228239
MockitoAnnotations.openMocks(this);
@@ -961,6 +972,7 @@ public void testUpdateExtension_RemovingUsedNetworkServiceThrows() {
961972

962973
when(networkOfferingServiceMapDao.listOfferingIdsByServiceAndProvider(Network.Service.StaticNat, "MyExt"))
963974
.thenReturn(Collections.singletonList(1L));
975+
when(networkOfferingDao.findById(1L)).thenReturn(mock(NetworkOfferingVO.class));
964976

965977
extensionsManager.updateExtension(cmd);
966978
}
@@ -992,10 +1004,42 @@ public void testUpdateExtension_RemovingServiceUsedByVpcOfferingThrows() {
9921004
.thenReturn(Collections.emptyList());
9931005
when(vpcOfferingServiceMapDao.listOfferingIdsByServiceAndProvider(Network.Service.StaticNat, "MyExt"))
9941006
.thenReturn(Collections.singletonList(1L));
1007+
when(vpcOfferingDao.findById(1L)).thenReturn(mock(VpcOfferingVO.class));
9951008

9961009
extensionsManager.updateExtension(cmd);
9971010
}
9981011

1012+
@Test
1013+
public void updateNetworkExtensionServicesOnPhysicalNetworks_IgnoresRemovedOfferings() {
1014+
ExtensionVO ext = mock(ExtensionVO.class);
1015+
when(ext.getId()).thenReturn(9L);
1016+
when(ext.getName()).thenReturn("MyExt");
1017+
1018+
// service map rows still reference offerings 1 (network) and 2 (VPC), but both offerings are removed
1019+
when(networkOfferingServiceMapDao.listOfferingIdsByServiceAndProvider(Network.Service.CustomAction, "MyExt"))
1020+
.thenReturn(Collections.singletonList(1L));
1021+
when(vpcOfferingServiceMapDao.listOfferingIdsByServiceAndProvider(Network.Service.CustomAction, "MyExt"))
1022+
.thenReturn(Collections.singletonList(2L));
1023+
when(networkOfferingDao.findById(1L)).thenReturn(null);
1024+
when(vpcOfferingDao.findById(2L)).thenReturn(null);
1025+
1026+
when(extensionResourceMapDao.listResourceIdsByExtensionIdAndType(9L, ExtensionResourceMap.ResourceType.PhysicalNetwork))
1027+
.thenReturn(Collections.singletonList(100L));
1028+
PhysicalNetworkServiceProviderVO nsp = mock(PhysicalNetworkServiceProviderVO.class);
1029+
when(nsp.getId()).thenReturn(500L);
1030+
when(nsp.getEnabledServices()).thenReturn(new ArrayList<>(Collections.singletonList(Network.Service.CustomAction)));
1031+
when(physicalNetworkServiceProviderDao.findByServiceProvider(100L, "MyExt")).thenReturn(nsp);
1032+
1033+
extensionsManager.updateNetworkExtensionServicesOnPhysicalNetworks(ext,
1034+
Set.of(Network.Service.CustomAction), Set.of(Network.Service.UserData));
1035+
1036+
ArgumentCaptor<List<Network.Service>> captor = ArgumentCaptor.forClass(List.class);
1037+
verify(nsp).setEnabledServices(captor.capture());
1038+
assertFalse(captor.getValue().contains(Network.Service.CustomAction));
1039+
assertTrue(captor.getValue().contains(Network.Service.UserData));
1040+
verify(physicalNetworkServiceProviderDao).update(500L, nsp);
1041+
}
1042+
9991043
@Test
10001044
public void testUpdateExtension_UpdatesPhysicalNetworkServicesWhenNotInUse() {
10011045
UpdateExtensionCmd cmd = mock(UpdateExtensionCmd.class);

0 commit comments

Comments
 (0)