Skip to content

Commit 0ca63f3

Browse files
authored
api,server,ui: allow cleaning up external details for host and serviceoffering (#11548)
1 parent 349feeb commit 0ca63f3

File tree

11 files changed

+125
-26
lines changed

11 files changed

+125
-26
lines changed

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,7 @@ public class ApiConstants {
11611161
public static final String OVM3_CLUSTER = "ovm3cluster";
11621162
public static final String OVM3_VIP = "ovm3vip";
11631163
public static final String CLEAN_UP_DETAILS = "cleanupdetails";
1164+
public static final String CLEAN_UP_EXTERNAL_DETAILS = "cleanupexternaldetails";
11641165
public static final String CLEAN_UP_PARAMETERS = "cleanupparameters";
11651166
public static final String VIRTUAL_SIZE = "virtualsize";
11661167
public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid";

api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ public class UpdateHostCmd extends BaseCmd {
7272
@Parameter(name = ApiConstants.EXTERNAL_DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", since = "4.21.0")
7373
protected Map externalDetails;
7474

75+
@Parameter(name = ApiConstants.CLEAN_UP_EXTERNAL_DETAILS,
76+
type = CommandType.BOOLEAN,
77+
description = "Optional boolean field, which indicates if external details should be cleaned up or not " +
78+
"(If set to true, external details removed for this host, externaldetails field ignored; " +
79+
"if false or not set, no action)",
80+
since = "4.22.0")
81+
protected Boolean cleanupExternalDetails;
82+
7583
/////////////////////////////////////////////////////
7684
/////////////////// Accessors ///////////////////////
7785
/////////////////////////////////////////////////////
@@ -112,6 +120,10 @@ public Map<String, String> getExternalDetails() {
112120
return convertExternalDetailsToMap(externalDetails);
113121
}
114122

123+
public boolean isCleanupExternalDetails() {
124+
return Boolean.TRUE.equals(cleanupExternalDetails);
125+
}
126+
115127
/////////////////////////////////////////////////////
116128
/////////////// API Implementation///////////////////
117129
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ public class UpdateServiceOfferingCmd extends BaseCmd {
101101
since = "4.21.0")
102102
private Map externalDetails;
103103

104+
@Parameter(name = ApiConstants.CLEAN_UP_EXTERNAL_DETAILS,
105+
type = CommandType.BOOLEAN,
106+
description = "Optional boolean field, which indicates if external details should be cleaned up or not " +
107+
"(If set to true, external details removed for this offering, externaldetails field ignored; " +
108+
"if false or not set, no action)",
109+
since = "4.22.0")
110+
protected Boolean cleanupExternalDetails;
111+
104112
/////////////////////////////////////////////////////
105113
/////////////////// Accessors ///////////////////////
106114
/////////////////////////////////////////////////////
@@ -205,6 +213,10 @@ public Map<String, String> getExternalDetails() {
205213
return convertExternalDetailsToMap(externalDetails);
206214
}
207215

216+
public boolean isCleanupExternalDetails() {
217+
return Boolean.TRUE.equals(cleanupExternalDetails);
218+
}
219+
208220
/////////////////////////////////////////////////////
209221
/////////////// API Implementation///////////////////
210222
/////////////////////////////////////////////////////

engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public interface HostDetailsDao extends GenericDao<DetailVO, Long> {
3333

3434
List<DetailVO> findByName(String name);
3535

36+
void removeExternalDetails(long hostId);
37+
3638
void replaceExternalDetails(long hostId, Map<String, String> details);
3739

3840
}

engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class HostDetailsDaoImpl extends GenericDaoBase<DetailVO, Long> implement
3939
protected final SearchBuilder<DetailVO> HostSearch;
4040
protected final SearchBuilder<DetailVO> DetailSearch;
4141
protected final SearchBuilder<DetailVO> DetailNameSearch;
42+
protected final SearchBuilder<DetailVO> ExternalDetailSearch;
4243

4344
public HostDetailsDaoImpl() {
4445
HostSearch = createSearchBuilder();
@@ -53,6 +54,11 @@ public HostDetailsDaoImpl() {
5354
DetailNameSearch = createSearchBuilder();
5455
DetailNameSearch.and("name", DetailNameSearch.entity().getName(), SearchCriteria.Op.EQ);
5556
DetailNameSearch.done();
57+
58+
ExternalDetailSearch = createSearchBuilder();
59+
ExternalDetailSearch.and("hostId", ExternalDetailSearch.entity().getHostId(), SearchCriteria.Op.EQ);
60+
ExternalDetailSearch.and("name", ExternalDetailSearch.entity().getName(), SearchCriteria.Op.LIKE);
61+
ExternalDetailSearch.done();
5662
}
5763

5864
@Override
@@ -133,6 +139,17 @@ public List<DetailVO> findByName(String name) {
133139
return listBy(sc);
134140
}
135141

142+
@Override
143+
public void removeExternalDetails(long hostId) {
144+
TransactionLegacy txn = TransactionLegacy.currentTxn();
145+
txn.start();
146+
SearchCriteria<DetailVO> sc = ExternalDetailSearch.create();
147+
sc.setParameters("hostId", hostId);
148+
sc.setParameters("name", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%");
149+
remove(sc);
150+
txn.commit();
151+
}
152+
136153
@Override
137154
public void replaceExternalDetails(long hostId, Map<String, String> details) {
138155
if (details.isEmpty()) {
@@ -149,11 +166,7 @@ public void replaceExternalDetails(long hostId, Map<String, String> details) {
149166
}
150167
detailVOs.add(new DetailVO(hostId, name, value));
151168
}
152-
SearchBuilder<DetailVO> sb = createSearchBuilder();
153-
sb.and("hostId", sb.entity().getHostId(), SearchCriteria.Op.EQ);
154-
sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE);
155-
sb.done();
156-
SearchCriteria<DetailVO> sc = sb.create();
169+
SearchCriteria<DetailVO> sc = ExternalDetailSearch.create();
157170
sc.setParameters("hostId", hostId);
158171
sc.setParameters("name", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%");
159172
remove(sc);

framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/api/UpdateExtensionCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class UpdateExtensionCmd extends BaseCmd {
7474
@Parameter(name = ApiConstants.CLEAN_UP_DETAILS,
7575
type = CommandType.BOOLEAN,
7676
description = "Optional boolean field, which indicates if details should be cleaned up or not " +
77-
"(If set to true, details removed for this action, details field ignored; " +
77+
"(If set to true, details removed for this extension, details field ignored; " +
7878
"if false or not set, no action)")
7979
private Boolean cleanupDetails;
8080

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3807,15 +3807,19 @@ private void setBytesRate(DiskOffering offering, Long bytesReadRate, Long bytesR
38073807
}
38083808

38093809
protected boolean serviceOfferingExternalDetailsNeedUpdate(final Map<String, String> offeringDetails,
3810-
final Map<String, String> externalDetails) {
3811-
if (MapUtils.isEmpty(externalDetails)) {
3810+
final Map<String, String> externalDetails, final boolean cleanupExternalDetails) {
3811+
if (MapUtils.isEmpty(externalDetails) && !cleanupExternalDetails) {
38123812
return false;
38133813
}
38143814

38153815
Map<String, String> existingExternalDetails = offeringDetails.entrySet().stream()
38163816
.filter(detail -> detail.getKey().startsWith(VmDetailConstants.EXTERNAL_DETAIL_PREFIX))
38173817
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
38183818

3819+
if (cleanupExternalDetails) {
3820+
return !MapUtils.isEmpty(existingExternalDetails);
3821+
}
3822+
38193823
if (MapUtils.isEmpty(existingExternalDetails) || existingExternalDetails.size() != externalDetails.size()) {
38203824
return true;
38213825
}
@@ -3845,6 +3849,7 @@ public ServiceOffering updateServiceOffering(final UpdateServiceOfferingCmd cmd)
38453849
ServiceOffering.State state = cmd.getState();
38463850
boolean purgeResources = cmd.isPurgeResources();
38473851
final Map<String, String> externalDetails = cmd.getExternalDetails();
3852+
final boolean cleanupExternalDetails = cmd.isCleanupExternalDetails();
38483853

38493854
if (userId == null) {
38503855
userId = Long.valueOf(User.UID_SYSTEM);
@@ -3938,7 +3943,7 @@ public ServiceOffering updateServiceOffering(final UpdateServiceOfferingCmd cmd)
39383943

39393944
final boolean updateNeeded = name != null || displayText != null || sortKey != null || storageTags != null || hostTags != null || state != null;
39403945
final boolean serviceOfferingExternalDetailsNeedUpdate =
3941-
serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails);
3946+
serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, cleanupExternalDetails);
39423947
final boolean detailsUpdateNeeded = !filteredDomainIds.equals(existingDomainIds) ||
39433948
!filteredZoneIds.equals(existingZoneIds) || purgeResources != existingPurgeResources ||
39443949
serviceOfferingExternalDetailsNeedUpdate;
@@ -4013,8 +4018,10 @@ public ServiceOffering updateServiceOffering(final UpdateServiceOfferingCmd cmd)
40134018
SearchCriteria<ServiceOfferingDetailsVO> externalDetailsRemoveSC = sb.create();
40144019
externalDetailsRemoveSC.setParameters("detailNameLike", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%");
40154020
_serviceOfferingDetailsDao.remove(externalDetailsRemoveSC);
4016-
for (Map.Entry<String, String> entry : externalDetails.entrySet()) {
4017-
detailsVO.add(new ServiceOfferingDetailsVO(id, entry.getKey(), entry.getValue(), true));
4021+
if (!cleanupExternalDetails) {
4022+
for (Map.Entry<String, String> entry : externalDetails.entrySet()) {
4023+
detailsVO.add(new ServiceOfferingDetailsVO(id, entry.getKey(), entry.getValue(), true));
4024+
}
40184025
}
40194026
}
40204027
}

server/src/main/java/com/cloud/resource/ResourceManagerImpl.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,12 +2799,15 @@ private void updateHostTags(HostVO host, Long hostId, List<String> hostTags, Boo
27992799

28002800
@Override
28012801
public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException {
2802-
return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(),
2803-
cmd.getAllocationState(), cmd.getUrl(), cmd.getHostTags(), cmd.getIsTagARule(), cmd.getAnnotation(), false, cmd.getExternalDetails());
2802+
return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(), cmd.getAllocationState(), cmd.getUrl(),
2803+
cmd.getHostTags(), cmd.getIsTagARule(), cmd.getAnnotation(), false,
2804+
cmd.getExternalDetails(), cmd.isCleanupExternalDetails());
28042805
}
28052806

28062807
private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String allocationState,
2807-
String url, List<String> hostTags, Boolean isTagARule, String annotation, boolean isUpdateFromHostHealthCheck, Map<String, String> externalDetails) throws NoTransitionException {
2808+
String url, List<String> hostTags, Boolean isTagARule, String annotation,
2809+
boolean isUpdateFromHostHealthCheck, Map<String, String> externalDetails,
2810+
boolean cleanupExternalDetails) throws NoTransitionException {
28082811
// Verify that the host exists
28092812
final HostVO host = _hostDao.findById(hostId);
28102813
if (host == null) {
@@ -2828,8 +2831,12 @@ private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String
28282831
updateHostTags(host, hostId, hostTags, isTagARule);
28292832
}
28302833

2831-
if (MapUtils.isNotEmpty(externalDetails)) {
2832-
_hostDetailsDao.replaceExternalDetails(hostId, externalDetails);
2834+
if (cleanupExternalDetails) {
2835+
_hostDetailsDao.removeExternalDetails(hostId);
2836+
} else {
2837+
if (MapUtils.isNotEmpty(externalDetails)) {
2838+
_hostDetailsDao.replaceExternalDetails(hostId, externalDetails);
2839+
}
28332840
}
28342841

28352842
if (url != null) {
@@ -2888,7 +2895,7 @@ private void sendAlertAndAnnotationForAutoEnableDisableKVMHostFeature(HostVO hos
28882895

28892896
@Override
28902897
public Host autoUpdateHostAllocationState(Long hostId, ResourceState.Event resourceEvent) throws NoTransitionException {
2891-
return updateHost(hostId, null, null, resourceEvent.toString(), null, null, null, null, true, null);
2898+
return updateHost(hostId, null, null, resourceEvent.toString(), null, null, null, null, true, null, false);
28922899
}
28932900

28942901
@Override

server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,17 +1028,47 @@ public void serviceOfferingExternalDetailsNeedUpdateReturnsFalseWhenExternalDeta
10281028
Map<String, String> offeringDetails = Map.of("key1", "value1");
10291029
Map<String, String> externalDetails = Collections.emptyMap();
10301030

1031-
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails);
1031+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, false);
10321032

10331033
Assert.assertFalse(result);
10341034
}
10351035

1036+
@Test
1037+
public void serviceOfferingExternalDetailsNeedUpdateReturnsFalseWhenExternalDetailsIsEmptyAndCleanupTrue() {
1038+
Map<String, String> offeringDetails = Map.of("key1", "value1");
1039+
Map<String, String> externalDetails = Collections.emptyMap();
1040+
1041+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, true);
1042+
1043+
Assert.assertFalse(result);
1044+
}
1045+
1046+
@Test
1047+
public void serviceOfferingExternalDetailsNeedUpdateReturnsTrueWhenExistingDetailsExistExternalDetailsIsEmptyAndCleanupTrue() {
1048+
Map<String, String> offeringDetails = Map.of("External:key1", "value1");
1049+
Map<String, String> externalDetails = Collections.emptyMap();
1050+
1051+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, true);
1052+
1053+
Assert.assertTrue(result);
1054+
}
1055+
1056+
@Test
1057+
public void serviceOfferingExternalDetailsNeedUpdateReturnsTrueWhenExistingExternalDetailsExistValidExternalDetailsAndCleanupTrue() {
1058+
Map<String, String> offeringDetails = Map.of("External:key1", "value1");
1059+
Map<String, String> externalDetails = Collections.emptyMap();
1060+
1061+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, true);
1062+
1063+
Assert.assertTrue(result);
1064+
}
1065+
10361066
@Test
10371067
public void serviceOfferingExternalDetailsNeedUpdateReturnsTrueWhenExistingExternalDetailsIsEmpty() {
10381068
Map<String, String> offeringDetails = Map.of("key1", "value1");
10391069
Map<String, String> externalDetails = Map.of("External:key1", "value1");
10401070

1041-
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails);
1071+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, false);
10421072

10431073
Assert.assertTrue(result);
10441074
}
@@ -1048,7 +1078,7 @@ public void serviceOfferingExternalDetailsNeedUpdateReturnsTrueWhenSizesDiffer()
10481078
Map<String, String> offeringDetails = Map.of("External:key1", "value1");
10491079
Map<String, String> externalDetails = Map.of("External:key1", "value1", "External:key2", "value2");
10501080

1051-
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails);
1081+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, false);
10521082

10531083
Assert.assertTrue(result);
10541084
}
@@ -1058,7 +1088,7 @@ public void serviceOfferingExternalDetailsNeedUpdateReturnsTrueWhenValuesDiffer(
10581088
Map<String, String> offeringDetails = Map.of("External:key1", "value1");
10591089
Map<String, String> externalDetails = Map.of("External:key1", "differentValue");
10601090

1061-
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails);
1091+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, false);
10621092

10631093
Assert.assertTrue(result);
10641094
}
@@ -1068,7 +1098,7 @@ public void serviceOfferingExternalDetailsNeedUpdateReturnsFalseWhenDetailsMatch
10681098
Map<String, String> offeringDetails = Map.of("External:key1", "value1", "External:key2", "value2");
10691099
Map<String, String> externalDetails = Map.of("External:key1", "value1", "External:key2", "value2");
10701100

1071-
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails);
1101+
boolean result = configurationManagerImplSpy.serviceOfferingExternalDetailsNeedUpdate(offeringDetails, externalDetails, false);
10721102

10731103
Assert.assertFalse(result);
10741104
}

ui/src/views/AutogenView.vue

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,9 +1770,22 @@ export default {
17701770
params[key] = param.opts[input].name
17711771
}
17721772
} else if (param.type === 'map' && typeof input === 'object') {
1773-
Object.entries(values.externaldetails).forEach(([key, value]) => {
1774-
params[param.name + '[0].' + key] = value
1775-
})
1773+
const details = values[key]
1774+
if (details && Object.keys(details).length > 0) {
1775+
Object.entries(details).forEach(([k, v]) => {
1776+
params[key + '[0].' + k] = v
1777+
})
1778+
} else {
1779+
if (['details', 'externaldetails'].includes(key)) {
1780+
const updateApiParams = this.$getApiParams(action.api)
1781+
const cleanupKey = 'cleanup' + key
1782+
if (cleanupKey in updateApiParams) {
1783+
params[cleanupKey] = true
1784+
break
1785+
}
1786+
}
1787+
params[key] = {}
1788+
}
17761789
} else {
17771790
params[key] = input
17781791
}

0 commit comments

Comments
 (0)