Skip to content

Commit 5433e77

Browse files
New feature: Implicit host tags (apache#8929)
* Merge two HostTagVO and HostTagDaoImpl * Implicit host tags * PR8929: add since * Update variable names * Update 8929: add unit test in LibvirtComputingResourceTest * Update 8929: add explicithosttags in response * Update 8929 UI: Update explicit host tags * Update 8929: remove host tags and change labels on UI * Update 8929: update host_view to use explicit_host_tags.is_tag_a_rule * Update: ui polish for host tags * Update 8929: fix UI error if no host tags
1 parent f1c3d2c commit 5433e77

File tree

29 files changed

+677
-251
lines changed

29 files changed

+677
-251
lines changed

agent/conf/agent.properties

+3
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,6 @@ iscsi.session.cleanup.enabled=false
430430
# If set to "true", the agent will register for libvirt domain events, allowing for immediate updates on crashed or
431431
# unexpectedly stopped. Experimental, requires agent restart.
432432
# libvirt.events.enabled=false
433+
434+
# Implicit host tags managed by agent.properties
435+
# host.tags=

agent/src/main/java/com/cloud/agent/properties/AgentProperties.java

+7
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,13 @@ public Property<Integer> getWorkers() {
803803
*/
804804
public static final Property<String> KEYSTORE_PASSPHRASE = new Property<>(KeyStoreUtils.KS_PASSPHRASE_PROPERTY, null, String.class);
805805

806+
/**
807+
* Implicit host tags
808+
* Data type: String.<br>
809+
* Default value: <code>null</code>
810+
*/
811+
public static final Property<String> HOST_TAGS = new Property<>("host.tags", null, String.class);
812+
806813
public static class Property <T>{
807814
private String name;
808815
private T defaultValue;

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

+1
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ public class ApiConstants {
265265
public static final String IS_EDGE = "isedge";
266266
public static final String IS_EXTRACTABLE = "isextractable";
267267
public static final String IS_FEATURED = "isfeatured";
268+
public static final String IS_IMPLICIT = "isimplicit";
268269
public static final String IS_PORTABLE = "isportable";
269270
public static final String IS_PUBLIC = "ispublic";
270271
public static final String IS_PERSISTENT = "ispersistent";

api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java

+16
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ public class HostForMigrationResponse extends BaseResponse {
208208
@Param(description = "comma-separated list of tags for the host")
209209
private String hostTags;
210210

211+
@SerializedName("explicithosttags")
212+
@Param(description = "comma-separated list of explicit host tags for the host", since = "4.20.0")
213+
private String explicitHostTags;
214+
215+
@SerializedName("implicithosttags")
216+
@Param(description = "comma-separated list of implicit host tags for the host", since = "4.20.0")
217+
private String implicitHostTags;
218+
211219
@SerializedName("hasenoughcapacity")
212220
@Param(description = "true if this host has enough CPU and RAM capacity to migrate a VM to it, false otherwise")
213221
private Boolean hasEnoughCapacity;
@@ -414,6 +422,14 @@ public void setHostTags(String hostTags) {
414422
this.hostTags = hostTags;
415423
}
416424

425+
public void setExplicitHostTags(String explicitHostTags) {
426+
this.explicitHostTags = explicitHostTags;
427+
}
428+
429+
public void setImplicitHostTags(String implicitHostTags) {
430+
this.implicitHostTags = implicitHostTags;
431+
}
432+
417433
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
418434
this.hasEnoughCapacity = hasEnoughCapacity;
419435
}

api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java

+24
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ public class HostResponse extends BaseResponseWithAnnotations {
221221
@Param(description = "comma-separated list of tags for the host")
222222
private String hostTags;
223223

224+
@SerializedName("explicithosttags")
225+
@Param(description = "comma-separated list of explicit host tags for the host", since = "4.20.0")
226+
private String explicitHostTags;
227+
228+
@SerializedName("implicithosttags")
229+
@Param(description = "comma-separated list of implicit host tags for the host", since = "4.20.0")
230+
private String implicitHostTags;
231+
224232
@SerializedName(ApiConstants.IS_TAG_A_RULE)
225233
@Param(description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE)
226234
private Boolean isTagARule;
@@ -458,6 +466,22 @@ public void setHostTags(String hostTags) {
458466
this.hostTags = hostTags;
459467
}
460468

469+
public String getExplicitHostTags() {
470+
return explicitHostTags;
471+
}
472+
473+
public void setExplicitHostTags(String explicitHostTags) {
474+
this.explicitHostTags = explicitHostTags;
475+
}
476+
477+
public String getImplicitHostTags() {
478+
return implicitHostTags;
479+
}
480+
481+
public void setImplicitHostTags(String implicitHostTags) {
482+
this.implicitHostTags = implicitHostTags;
483+
}
484+
461485
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
462486
this.hasEnoughCapacity = hasEnoughCapacity;
463487
}

api/src/main/java/org/apache/cloudstack/api/response/HostTagResponse.java

+13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.gson.annotations.SerializedName;
2020
import com.cloud.serializer.Param;
2121

22+
import org.apache.cloudstack.api.ApiConstants;
2223
import org.apache.cloudstack.api.BaseResponse;
2324

2425
public class HostTagResponse extends BaseResponse {
@@ -34,6 +35,10 @@ public class HostTagResponse extends BaseResponse {
3435
@Param(description = "the name of the host tag")
3536
private String name;
3637

38+
@SerializedName(ApiConstants.IS_IMPLICIT)
39+
@Param(description = "true if the host tag is implicit", since = "4.20.0")
40+
private boolean isImplicit;
41+
3742
public String getId() {
3843
return id;
3944
}
@@ -57,4 +62,12 @@ public String getName() {
5762
public void setName(String name) {
5863
this.name = name;
5964
}
65+
66+
public boolean isImplicit() {
67+
return isImplicit;
68+
}
69+
70+
public void setImplicit(boolean implicit) {
71+
isImplicit = implicit;
72+
}
6073
}

core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java

+4
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ public void setHostTags(String hostTag) {
174174
this.hostTags.add(hostTag);
175175
}
176176

177+
public void setHostTags(List<String> hostTags) {
178+
this.hostTags = hostTags;
179+
}
180+
177181
public HashMap<String, HashMap<String, VgpuTypesInfo>> getGpuGroupDetails() {
178182
return groupDetails;
179183
}

engine/schema/src/main/java/com/cloud/host/HostTagVO.java

+10
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public class HostTagVO implements InternalIdentity {
4040
@Column(name = "tag")
4141
private String tag;
4242

43+
@Column(name = "is_implicit")
44+
private boolean isImplicit = false;
45+
4346
@Column(name = "is_tag_a_rule")
4447
private boolean isTagARule;
4548

@@ -74,6 +77,13 @@ public boolean getIsTagARule() {
7477
return isTagARule;
7578
}
7679

80+
public void setIsImplicit(boolean isImplicit) {
81+
this.isImplicit = isImplicit;
82+
}
83+
84+
public boolean getIsImplicit() {
85+
return isImplicit;
86+
}
7787

7888
@Override
7989
public long getId() {

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

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.cloud.host.HostTagVO;
2222
import com.cloud.utils.db.GenericDao;
23+
import org.apache.cloudstack.api.response.HostTagResponse;
2324
import org.apache.cloudstack.framework.config.ConfigKey;
2425

2526
public interface HostTagsDao extends GenericDao<HostTagVO, Long> {
@@ -35,6 +36,13 @@ public interface HostTagsDao extends GenericDao<HostTagVO, Long> {
3536

3637
void deleteTags(long hostId);
3738

39+
boolean updateImplicitTags(long hostId, List<String> hostTags);
40+
41+
List<HostTagVO> getExplicitHostTags(long hostId);
42+
3843
List<HostTagVO> findHostRuleTags();
3944

45+
HostTagResponse newHostTagResponse(HostTagVO hostTag);
46+
47+
List<HostTagVO> searchByIds(Long... hostTagIds);
4048
}

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

+125
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616
// under the License.
1717
package com.cloud.host.dao;
1818

19+
import java.util.ArrayList;
1920
import java.util.List;
2021

22+
import org.apache.cloudstack.api.response.HostTagResponse;
2123
import org.apache.cloudstack.framework.config.ConfigKey;
2224
import org.apache.cloudstack.framework.config.Configurable;
25+
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
26+
import org.apache.commons.lang3.StringUtils;
2327
import org.springframework.stereotype.Component;
2428

2529
import com.cloud.host.HostTagVO;
@@ -30,14 +34,23 @@
3034
import com.cloud.utils.db.TransactionLegacy;
3135
import com.cloud.utils.db.SearchCriteria.Func;
3236

37+
import javax.inject.Inject;
38+
3339
@Component
3440
public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements HostTagsDao, Configurable {
3541
protected final SearchBuilder<HostTagVO> HostSearch;
3642
protected final GenericSearchBuilder<HostTagVO, String> DistinctImplictTagsSearch;
43+
private final SearchBuilder<HostTagVO> stSearch;
44+
private final SearchBuilder<HostTagVO> tagIdsearch;
45+
private final SearchBuilder<HostTagVO> ImplicitTagsSearch;
46+
47+
@Inject
48+
private ConfigurationDao _configDao;
3749

3850
public HostTagsDaoImpl() {
3951
HostSearch = createSearchBuilder();
4052
HostSearch.and("hostId", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ);
53+
HostSearch.and("isImplicit", HostSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ);
4154
HostSearch.and("isTagARule", HostSearch.entity().getIsTagARule(), SearchCriteria.Op.EQ);
4255
HostSearch.done();
4356

@@ -46,6 +59,19 @@ public HostTagsDaoImpl() {
4659
DistinctImplictTagsSearch.and("hostIds", DistinctImplictTagsSearch.entity().getHostId(), SearchCriteria.Op.IN);
4760
DistinctImplictTagsSearch.and("implicitTags", DistinctImplictTagsSearch.entity().getTag(), SearchCriteria.Op.IN);
4861
DistinctImplictTagsSearch.done();
62+
63+
stSearch = createSearchBuilder();
64+
stSearch.and("idIN", stSearch.entity().getId(), SearchCriteria.Op.IN);
65+
stSearch.done();
66+
67+
tagIdsearch = createSearchBuilder();
68+
tagIdsearch.and("id", tagIdsearch.entity().getId(), SearchCriteria.Op.EQ);
69+
tagIdsearch.done();
70+
71+
ImplicitTagsSearch = createSearchBuilder();
72+
ImplicitTagsSearch.and("hostId", ImplicitTagsSearch.entity().getHostId(), SearchCriteria.Op.EQ);
73+
ImplicitTagsSearch.and("isImplicit", ImplicitTagsSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ);
74+
ImplicitTagsSearch.done();
4975
}
5076

5177
@Override
@@ -74,6 +100,36 @@ public void deleteTags(long hostId) {
74100
txn.commit();
75101
}
76102

103+
@Override
104+
public boolean updateImplicitTags(long hostId, List<String> hostTags) {
105+
TransactionLegacy txn = TransactionLegacy.currentTxn();
106+
txn.start();
107+
SearchCriteria<HostTagVO> sc = ImplicitTagsSearch.create();
108+
sc.setParameters("hostId", hostId);
109+
sc.setParameters("isImplicit", true);
110+
boolean expunged = expunge(sc) > 0;
111+
boolean persisted = false;
112+
for (String tag : hostTags) {
113+
if (StringUtils.isNotBlank(tag)) {
114+
HostTagVO vo = new HostTagVO(hostId, tag.trim());
115+
vo.setIsImplicit(true);
116+
persist(vo);
117+
persisted = true;
118+
}
119+
}
120+
txn.commit();
121+
return expunged || persisted;
122+
}
123+
124+
@Override
125+
public List<HostTagVO> getExplicitHostTags(long hostId) {
126+
SearchCriteria<HostTagVO> sc = ImplicitTagsSearch.create();
127+
sc.setParameters("hostId", hostId);
128+
sc.setParameters("isImplicit", false);
129+
130+
return search(sc, null);
131+
}
132+
77133
@Override
78134
public List<HostTagVO> findHostRuleTags() {
79135
SearchCriteria<HostTagVO> sc = HostSearch.create();
@@ -89,6 +145,7 @@ public void persist(long hostId, List<String> hostTags, Boolean isTagARule) {
89145
txn.start();
90146
SearchCriteria<HostTagVO> sc = HostSearch.create();
91147
sc.setParameters("hostId", hostId);
148+
sc.setParameters("isImplicit", false);
92149
expunge(sc);
93150

94151
for (String tag : hostTags) {
@@ -110,4 +167,72 @@ public ConfigKey<?>[] getConfigKeys() {
110167
public String getConfigComponentName() {
111168
return HostTagsDaoImpl.class.getSimpleName();
112169
}
170+
171+
@Override
172+
public HostTagResponse newHostTagResponse(HostTagVO tag) {
173+
HostTagResponse tagResponse = new HostTagResponse();
174+
175+
tagResponse.setName(tag.getTag());
176+
tagResponse.setHostId(tag.getHostId());
177+
tagResponse.setImplicit(tag.getIsImplicit());
178+
179+
tagResponse.setObjectName("hosttag");
180+
181+
return tagResponse;
182+
}
183+
184+
@Override
185+
public List<HostTagVO> searchByIds(Long... tagIds) {
186+
String batchCfg = _configDao.getValue("detail.batch.query.size");
187+
188+
final int detailsBatchSize = batchCfg != null ? Integer.parseInt(batchCfg) : 2000;
189+
190+
// query details by batches
191+
List<HostTagVO> tagList = new ArrayList<>();
192+
int curr_index = 0;
193+
194+
if (tagIds.length > detailsBatchSize) {
195+
while ((curr_index + detailsBatchSize) <= tagIds.length) {
196+
Long[] ids = new Long[detailsBatchSize];
197+
198+
for (int k = 0, j = curr_index; j < curr_index + detailsBatchSize; j++, k++) {
199+
ids[k] = tagIds[j];
200+
}
201+
202+
SearchCriteria<HostTagVO> sc = stSearch.create();
203+
204+
sc.setParameters("idIN", (Object[])ids);
205+
206+
List<HostTagVO> vms = searchIncludingRemoved(sc, null, null, false);
207+
208+
if (vms != null) {
209+
tagList.addAll(vms);
210+
}
211+
212+
curr_index += detailsBatchSize;
213+
}
214+
}
215+
216+
if (curr_index < tagIds.length) {
217+
int batch_size = (tagIds.length - curr_index);
218+
// set the ids value
219+
Long[] ids = new Long[batch_size];
220+
221+
for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) {
222+
ids[k] = tagIds[j];
223+
}
224+
225+
SearchCriteria<HostTagVO> sc = stSearch.create();
226+
227+
sc.setParameters("idIN", (Object[])ids);
228+
229+
List<HostTagVO> tags = searchIncludingRemoved(sc, null, null, false);
230+
231+
if (tags != null) {
232+
tagList.addAll(tags);
233+
}
234+
}
235+
236+
return tagList;
237+
}
113238
}

engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml

-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@
187187
<bean id="storageNetworkIpAddressDaoImpl" class="com.cloud.dc.dao.StorageNetworkIpAddressDaoImpl" />
188188
<bean id="storageNetworkIpRangeDaoImpl" class="com.cloud.dc.dao.StorageNetworkIpRangeDaoImpl" />
189189
<bean id="storagePoolJoinDaoImpl" class="com.cloud.api.query.dao.StoragePoolJoinDaoImpl" />
190-
<bean id="hostTagDaoImpl" class="com.cloud.api.query.dao.HostTagDaoImpl" />
191190
<bean id="storagePoolWorkDaoImpl" class="com.cloud.storage.dao.StoragePoolWorkDaoImpl" />
192191
<bean id="uploadDaoImpl" class="com.cloud.storage.dao.UploadDaoImpl" />
193192
<bean id="usageDaoImpl" class="com.cloud.usage.dao.UsageDaoImpl" />

engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql

+3
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,6 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`(
7979
PRIMARY KEY (`account_id`, `email_template_id`),
8080
CONSTRAINT `FK_quota_email_configuration_account_id` FOREIGN KEY (`account_id`) REFERENCES `cloud_usage`.`quota_account`(`account_id`),
8181
CONSTRAINT `FK_quota_email_configuration_email_template_id` FOREIGN KEY (`email_template_id`) REFERENCES `cloud_usage`.`quota_email_templates`(`id`));
82+
83+
-- Add `is_implicit` column to `host_tags` table
84+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host_tags', 'is_implicit', 'int(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT "If host tag is implicit or explicit" ');

engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql

+7-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ SELECT
5353
host_pod_ref.uuid pod_uuid,
5454
host_pod_ref.name pod_name,
5555
GROUP_CONCAT(DISTINCT(host_tags.tag)) AS tag,
56-
`host_tags`.`is_tag_a_rule` AS `is_tag_a_rule`,
56+
GROUP_CONCAT(DISTINCT(explicit_host_tags.tag)) AS explicit_tag,
57+
GROUP_CONCAT(DISTINCT(implicit_host_tags.tag)) AS implicit_tag,
58+
`explicit_host_tags`.`is_tag_a_rule` AS `is_tag_a_rule`,
5759
guest_os_category.id guest_os_category_id,
5860
guest_os_category.uuid guest_os_category_uuid,
5961
guest_os_category.name guest_os_category_name,
@@ -89,6 +91,10 @@ FROM
8991
LEFT JOIN
9092
`cloud`.`host_tags` ON host_tags.host_id = host.id
9193
LEFT JOIN
94+
`cloud`.`host_tags` AS explicit_host_tags ON explicit_host_tags.host_id = host.id AND explicit_host_tags.is_implicit = 0
95+
LEFT JOIN
96+
`cloud`.`host_tags` AS implicit_host_tags ON implicit_host_tags.host_id = host.id AND implicit_host_tags.is_implicit = 1
97+
LEFT JOIN
9298
`cloud`.`op_host_capacity` mem_caps ON host.id = mem_caps.host_id
9399
AND mem_caps.capacity_type = 0
94100
LEFT JOIN

0 commit comments

Comments
 (0)