mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
New feature: Implicit host tags (#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
This commit is contained in:
parent
f1c3d2c4be
commit
5433e775e5
@ -430,3 +430,6 @@ iscsi.session.cleanup.enabled=false
|
|||||||
# If set to "true", the agent will register for libvirt domain events, allowing for immediate updates on crashed or
|
# If set to "true", the agent will register for libvirt domain events, allowing for immediate updates on crashed or
|
||||||
# unexpectedly stopped. Experimental, requires agent restart.
|
# unexpectedly stopped. Experimental, requires agent restart.
|
||||||
# libvirt.events.enabled=false
|
# libvirt.events.enabled=false
|
||||||
|
|
||||||
|
# Implicit host tags managed by agent.properties
|
||||||
|
# host.tags=
|
||||||
|
|||||||
@ -803,6 +803,13 @@ public class AgentProperties{
|
|||||||
*/
|
*/
|
||||||
public static final Property<String> KEYSTORE_PASSPHRASE = new Property<>(KeyStoreUtils.KS_PASSPHRASE_PROPERTY, null, String.class);
|
public static final Property<String> KEYSTORE_PASSPHRASE = new Property<>(KeyStoreUtils.KS_PASSPHRASE_PROPERTY, null, String.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implicit host tags
|
||||||
|
* Data type: String.<br>
|
||||||
|
* Default value: <code>null</code>
|
||||||
|
*/
|
||||||
|
public static final Property<String> HOST_TAGS = new Property<>("host.tags", null, String.class);
|
||||||
|
|
||||||
public static class Property <T>{
|
public static class Property <T>{
|
||||||
private String name;
|
private String name;
|
||||||
private T defaultValue;
|
private T defaultValue;
|
||||||
|
|||||||
@ -265,6 +265,7 @@ public class ApiConstants {
|
|||||||
public static final String IS_EDGE = "isedge";
|
public static final String IS_EDGE = "isedge";
|
||||||
public static final String IS_EXTRACTABLE = "isextractable";
|
public static final String IS_EXTRACTABLE = "isextractable";
|
||||||
public static final String IS_FEATURED = "isfeatured";
|
public static final String IS_FEATURED = "isfeatured";
|
||||||
|
public static final String IS_IMPLICIT = "isimplicit";
|
||||||
public static final String IS_PORTABLE = "isportable";
|
public static final String IS_PORTABLE = "isportable";
|
||||||
public static final String IS_PUBLIC = "ispublic";
|
public static final String IS_PUBLIC = "ispublic";
|
||||||
public static final String IS_PERSISTENT = "ispersistent";
|
public static final String IS_PERSISTENT = "ispersistent";
|
||||||
|
|||||||
@ -208,6 +208,14 @@ public class HostForMigrationResponse extends BaseResponse {
|
|||||||
@Param(description = "comma-separated list of tags for the host")
|
@Param(description = "comma-separated list of tags for the host")
|
||||||
private String hostTags;
|
private String hostTags;
|
||||||
|
|
||||||
|
@SerializedName("explicithosttags")
|
||||||
|
@Param(description = "comma-separated list of explicit host tags for the host", since = "4.20.0")
|
||||||
|
private String explicitHostTags;
|
||||||
|
|
||||||
|
@SerializedName("implicithosttags")
|
||||||
|
@Param(description = "comma-separated list of implicit host tags for the host", since = "4.20.0")
|
||||||
|
private String implicitHostTags;
|
||||||
|
|
||||||
@SerializedName("hasenoughcapacity")
|
@SerializedName("hasenoughcapacity")
|
||||||
@Param(description = "true if this host has enough CPU and RAM capacity to migrate a VM to it, false otherwise")
|
@Param(description = "true if this host has enough CPU and RAM capacity to migrate a VM to it, false otherwise")
|
||||||
private Boolean hasEnoughCapacity;
|
private Boolean hasEnoughCapacity;
|
||||||
@ -414,6 +422,14 @@ public class HostForMigrationResponse extends BaseResponse {
|
|||||||
this.hostTags = hostTags;
|
this.hostTags = hostTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setExplicitHostTags(String explicitHostTags) {
|
||||||
|
this.explicitHostTags = explicitHostTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImplicitHostTags(String implicitHostTags) {
|
||||||
|
this.implicitHostTags = implicitHostTags;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
|
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
|
||||||
this.hasEnoughCapacity = hasEnoughCapacity;
|
this.hasEnoughCapacity = hasEnoughCapacity;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -221,6 +221,14 @@ public class HostResponse extends BaseResponseWithAnnotations {
|
|||||||
@Param(description = "comma-separated list of tags for the host")
|
@Param(description = "comma-separated list of tags for the host")
|
||||||
private String hostTags;
|
private String hostTags;
|
||||||
|
|
||||||
|
@SerializedName("explicithosttags")
|
||||||
|
@Param(description = "comma-separated list of explicit host tags for the host", since = "4.20.0")
|
||||||
|
private String explicitHostTags;
|
||||||
|
|
||||||
|
@SerializedName("implicithosttags")
|
||||||
|
@Param(description = "comma-separated list of implicit host tags for the host", since = "4.20.0")
|
||||||
|
private String implicitHostTags;
|
||||||
|
|
||||||
@SerializedName(ApiConstants.IS_TAG_A_RULE)
|
@SerializedName(ApiConstants.IS_TAG_A_RULE)
|
||||||
@Param(description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE)
|
@Param(description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE)
|
||||||
private Boolean isTagARule;
|
private Boolean isTagARule;
|
||||||
@ -458,6 +466,22 @@ public class HostResponse extends BaseResponseWithAnnotations {
|
|||||||
this.hostTags = hostTags;
|
this.hostTags = hostTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getExplicitHostTags() {
|
||||||
|
return explicitHostTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExplicitHostTags(String explicitHostTags) {
|
||||||
|
this.explicitHostTags = explicitHostTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImplicitHostTags() {
|
||||||
|
return implicitHostTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImplicitHostTags(String implicitHostTags) {
|
||||||
|
this.implicitHostTags = implicitHostTags;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
|
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
|
||||||
this.hasEnoughCapacity = hasEnoughCapacity;
|
this.hasEnoughCapacity = hasEnoughCapacity;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package org.apache.cloudstack.api.response;
|
|||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.cloud.serializer.Param;
|
import com.cloud.serializer.Param;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseResponse;
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
|
||||||
public class HostTagResponse extends BaseResponse {
|
public class HostTagResponse extends BaseResponse {
|
||||||
@ -34,6 +35,10 @@ public class HostTagResponse extends BaseResponse {
|
|||||||
@Param(description = "the name of the host tag")
|
@Param(description = "the name of the host tag")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.IS_IMPLICIT)
|
||||||
|
@Param(description = "true if the host tag is implicit", since = "4.20.0")
|
||||||
|
private boolean isImplicit;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -57,4 +62,12 @@ public class HostTagResponse extends BaseResponse {
|
|||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isImplicit() {
|
||||||
|
return isImplicit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImplicit(boolean implicit) {
|
||||||
|
isImplicit = implicit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -174,6 +174,10 @@ public class StartupRoutingCommand extends StartupCommand {
|
|||||||
this.hostTags.add(hostTag);
|
this.hostTags.add(hostTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHostTags(List<String> hostTags) {
|
||||||
|
this.hostTags = hostTags;
|
||||||
|
}
|
||||||
|
|
||||||
public HashMap<String, HashMap<String, VgpuTypesInfo>> getGpuGroupDetails() {
|
public HashMap<String, HashMap<String, VgpuTypesInfo>> getGpuGroupDetails() {
|
||||||
return groupDetails;
|
return groupDetails;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,9 @@ public class HostTagVO implements InternalIdentity {
|
|||||||
@Column(name = "tag")
|
@Column(name = "tag")
|
||||||
private String tag;
|
private String tag;
|
||||||
|
|
||||||
|
@Column(name = "is_implicit")
|
||||||
|
private boolean isImplicit = false;
|
||||||
|
|
||||||
@Column(name = "is_tag_a_rule")
|
@Column(name = "is_tag_a_rule")
|
||||||
private boolean isTagARule;
|
private boolean isTagARule;
|
||||||
|
|
||||||
@ -74,6 +77,13 @@ public class HostTagVO implements InternalIdentity {
|
|||||||
return isTagARule;
|
return isTagARule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setIsImplicit(boolean isImplicit) {
|
||||||
|
this.isImplicit = isImplicit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getIsImplicit() {
|
||||||
|
return isImplicit;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getId() {
|
public long getId() {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.cloud.host.HostTagVO;
|
import com.cloud.host.HostTagVO;
|
||||||
import com.cloud.utils.db.GenericDao;
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
import org.apache.cloudstack.api.response.HostTagResponse;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
|
||||||
public interface HostTagsDao extends GenericDao<HostTagVO, Long> {
|
public interface HostTagsDao extends GenericDao<HostTagVO, Long> {
|
||||||
@ -35,6 +36,13 @@ public interface HostTagsDao extends GenericDao<HostTagVO, Long> {
|
|||||||
|
|
||||||
void deleteTags(long hostId);
|
void deleteTags(long hostId);
|
||||||
|
|
||||||
|
boolean updateImplicitTags(long hostId, List<String> hostTags);
|
||||||
|
|
||||||
|
List<HostTagVO> getExplicitHostTags(long hostId);
|
||||||
|
|
||||||
List<HostTagVO> findHostRuleTags();
|
List<HostTagVO> findHostRuleTags();
|
||||||
|
|
||||||
|
HostTagResponse newHostTagResponse(HostTagVO hostTag);
|
||||||
|
|
||||||
|
List<HostTagVO> searchByIds(Long... hostTagIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,10 +16,14 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.host.dao;
|
package com.cloud.host.dao;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.response.HostTagResponse;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
import org.apache.cloudstack.framework.config.Configurable;
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.cloud.host.HostTagVO;
|
import com.cloud.host.HostTagVO;
|
||||||
@ -30,14 +34,23 @@ import com.cloud.utils.db.SearchCriteria;
|
|||||||
import com.cloud.utils.db.TransactionLegacy;
|
import com.cloud.utils.db.TransactionLegacy;
|
||||||
import com.cloud.utils.db.SearchCriteria.Func;
|
import com.cloud.utils.db.SearchCriteria.Func;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements HostTagsDao, Configurable {
|
public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements HostTagsDao, Configurable {
|
||||||
protected final SearchBuilder<HostTagVO> HostSearch;
|
protected final SearchBuilder<HostTagVO> HostSearch;
|
||||||
protected final GenericSearchBuilder<HostTagVO, String> DistinctImplictTagsSearch;
|
protected final GenericSearchBuilder<HostTagVO, String> DistinctImplictTagsSearch;
|
||||||
|
private final SearchBuilder<HostTagVO> stSearch;
|
||||||
|
private final SearchBuilder<HostTagVO> tagIdsearch;
|
||||||
|
private final SearchBuilder<HostTagVO> ImplicitTagsSearch;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ConfigurationDao _configDao;
|
||||||
|
|
||||||
public HostTagsDaoImpl() {
|
public HostTagsDaoImpl() {
|
||||||
HostSearch = createSearchBuilder();
|
HostSearch = createSearchBuilder();
|
||||||
HostSearch.and("hostId", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ);
|
HostSearch.and("hostId", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ);
|
||||||
|
HostSearch.and("isImplicit", HostSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ);
|
||||||
HostSearch.and("isTagARule", HostSearch.entity().getIsTagARule(), SearchCriteria.Op.EQ);
|
HostSearch.and("isTagARule", HostSearch.entity().getIsTagARule(), SearchCriteria.Op.EQ);
|
||||||
HostSearch.done();
|
HostSearch.done();
|
||||||
|
|
||||||
@ -46,6 +59,19 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
|
|||||||
DistinctImplictTagsSearch.and("hostIds", DistinctImplictTagsSearch.entity().getHostId(), SearchCriteria.Op.IN);
|
DistinctImplictTagsSearch.and("hostIds", DistinctImplictTagsSearch.entity().getHostId(), SearchCriteria.Op.IN);
|
||||||
DistinctImplictTagsSearch.and("implicitTags", DistinctImplictTagsSearch.entity().getTag(), SearchCriteria.Op.IN);
|
DistinctImplictTagsSearch.and("implicitTags", DistinctImplictTagsSearch.entity().getTag(), SearchCriteria.Op.IN);
|
||||||
DistinctImplictTagsSearch.done();
|
DistinctImplictTagsSearch.done();
|
||||||
|
|
||||||
|
stSearch = createSearchBuilder();
|
||||||
|
stSearch.and("idIN", stSearch.entity().getId(), SearchCriteria.Op.IN);
|
||||||
|
stSearch.done();
|
||||||
|
|
||||||
|
tagIdsearch = createSearchBuilder();
|
||||||
|
tagIdsearch.and("id", tagIdsearch.entity().getId(), SearchCriteria.Op.EQ);
|
||||||
|
tagIdsearch.done();
|
||||||
|
|
||||||
|
ImplicitTagsSearch = createSearchBuilder();
|
||||||
|
ImplicitTagsSearch.and("hostId", ImplicitTagsSearch.entity().getHostId(), SearchCriteria.Op.EQ);
|
||||||
|
ImplicitTagsSearch.and("isImplicit", ImplicitTagsSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ);
|
||||||
|
ImplicitTagsSearch.done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -74,6 +100,36 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
|
|||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateImplicitTags(long hostId, List<String> hostTags) {
|
||||||
|
TransactionLegacy txn = TransactionLegacy.currentTxn();
|
||||||
|
txn.start();
|
||||||
|
SearchCriteria<HostTagVO> sc = ImplicitTagsSearch.create();
|
||||||
|
sc.setParameters("hostId", hostId);
|
||||||
|
sc.setParameters("isImplicit", true);
|
||||||
|
boolean expunged = expunge(sc) > 0;
|
||||||
|
boolean persisted = false;
|
||||||
|
for (String tag : hostTags) {
|
||||||
|
if (StringUtils.isNotBlank(tag)) {
|
||||||
|
HostTagVO vo = new HostTagVO(hostId, tag.trim());
|
||||||
|
vo.setIsImplicit(true);
|
||||||
|
persist(vo);
|
||||||
|
persisted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
txn.commit();
|
||||||
|
return expunged || persisted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HostTagVO> getExplicitHostTags(long hostId) {
|
||||||
|
SearchCriteria<HostTagVO> sc = ImplicitTagsSearch.create();
|
||||||
|
sc.setParameters("hostId", hostId);
|
||||||
|
sc.setParameters("isImplicit", false);
|
||||||
|
|
||||||
|
return search(sc, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<HostTagVO> findHostRuleTags() {
|
public List<HostTagVO> findHostRuleTags() {
|
||||||
SearchCriteria<HostTagVO> sc = HostSearch.create();
|
SearchCriteria<HostTagVO> sc = HostSearch.create();
|
||||||
@ -89,6 +145,7 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
|
|||||||
txn.start();
|
txn.start();
|
||||||
SearchCriteria<HostTagVO> sc = HostSearch.create();
|
SearchCriteria<HostTagVO> sc = HostSearch.create();
|
||||||
sc.setParameters("hostId", hostId);
|
sc.setParameters("hostId", hostId);
|
||||||
|
sc.setParameters("isImplicit", false);
|
||||||
expunge(sc);
|
expunge(sc);
|
||||||
|
|
||||||
for (String tag : hostTags) {
|
for (String tag : hostTags) {
|
||||||
@ -110,4 +167,72 @@ public class HostTagsDaoImpl extends GenericDaoBase<HostTagVO, Long> implements
|
|||||||
public String getConfigComponentName() {
|
public String getConfigComponentName() {
|
||||||
return HostTagsDaoImpl.class.getSimpleName();
|
return HostTagsDaoImpl.class.getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HostTagResponse newHostTagResponse(HostTagVO tag) {
|
||||||
|
HostTagResponse tagResponse = new HostTagResponse();
|
||||||
|
|
||||||
|
tagResponse.setName(tag.getTag());
|
||||||
|
tagResponse.setHostId(tag.getHostId());
|
||||||
|
tagResponse.setImplicit(tag.getIsImplicit());
|
||||||
|
|
||||||
|
tagResponse.setObjectName("hosttag");
|
||||||
|
|
||||||
|
return tagResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HostTagVO> searchByIds(Long... tagIds) {
|
||||||
|
String batchCfg = _configDao.getValue("detail.batch.query.size");
|
||||||
|
|
||||||
|
final int detailsBatchSize = batchCfg != null ? Integer.parseInt(batchCfg) : 2000;
|
||||||
|
|
||||||
|
// query details by batches
|
||||||
|
List<HostTagVO> tagList = new ArrayList<>();
|
||||||
|
int curr_index = 0;
|
||||||
|
|
||||||
|
if (tagIds.length > detailsBatchSize) {
|
||||||
|
while ((curr_index + detailsBatchSize) <= tagIds.length) {
|
||||||
|
Long[] ids = new Long[detailsBatchSize];
|
||||||
|
|
||||||
|
for (int k = 0, j = curr_index; j < curr_index + detailsBatchSize; j++, k++) {
|
||||||
|
ids[k] = tagIds[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchCriteria<HostTagVO> sc = stSearch.create();
|
||||||
|
|
||||||
|
sc.setParameters("idIN", (Object[])ids);
|
||||||
|
|
||||||
|
List<HostTagVO> vms = searchIncludingRemoved(sc, null, null, false);
|
||||||
|
|
||||||
|
if (vms != null) {
|
||||||
|
tagList.addAll(vms);
|
||||||
|
}
|
||||||
|
|
||||||
|
curr_index += detailsBatchSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr_index < tagIds.length) {
|
||||||
|
int batch_size = (tagIds.length - curr_index);
|
||||||
|
// set the ids value
|
||||||
|
Long[] ids = new Long[batch_size];
|
||||||
|
|
||||||
|
for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) {
|
||||||
|
ids[k] = tagIds[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchCriteria<HostTagVO> sc = stSearch.create();
|
||||||
|
|
||||||
|
sc.setParameters("idIN", (Object[])ids);
|
||||||
|
|
||||||
|
List<HostTagVO> tags = searchIncludingRemoved(sc, null, null, false);
|
||||||
|
|
||||||
|
if (tags != null) {
|
||||||
|
tagList.addAll(tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -187,7 +187,6 @@
|
|||||||
<bean id="storageNetworkIpAddressDaoImpl" class="com.cloud.dc.dao.StorageNetworkIpAddressDaoImpl" />
|
<bean id="storageNetworkIpAddressDaoImpl" class="com.cloud.dc.dao.StorageNetworkIpAddressDaoImpl" />
|
||||||
<bean id="storageNetworkIpRangeDaoImpl" class="com.cloud.dc.dao.StorageNetworkIpRangeDaoImpl" />
|
<bean id="storageNetworkIpRangeDaoImpl" class="com.cloud.dc.dao.StorageNetworkIpRangeDaoImpl" />
|
||||||
<bean id="storagePoolJoinDaoImpl" class="com.cloud.api.query.dao.StoragePoolJoinDaoImpl" />
|
<bean id="storagePoolJoinDaoImpl" class="com.cloud.api.query.dao.StoragePoolJoinDaoImpl" />
|
||||||
<bean id="hostTagDaoImpl" class="com.cloud.api.query.dao.HostTagDaoImpl" />
|
|
||||||
<bean id="storagePoolWorkDaoImpl" class="com.cloud.storage.dao.StoragePoolWorkDaoImpl" />
|
<bean id="storagePoolWorkDaoImpl" class="com.cloud.storage.dao.StoragePoolWorkDaoImpl" />
|
||||||
<bean id="uploadDaoImpl" class="com.cloud.storage.dao.UploadDaoImpl" />
|
<bean id="uploadDaoImpl" class="com.cloud.storage.dao.UploadDaoImpl" />
|
||||||
<bean id="usageDaoImpl" class="com.cloud.usage.dao.UsageDaoImpl" />
|
<bean id="usageDaoImpl" class="com.cloud.usage.dao.UsageDaoImpl" />
|
||||||
|
|||||||
@ -79,3 +79,6 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`(
|
|||||||
PRIMARY KEY (`account_id`, `email_template_id`),
|
PRIMARY KEY (`account_id`, `email_template_id`),
|
||||||
CONSTRAINT `FK_quota_email_configuration_account_id` FOREIGN KEY (`account_id`) REFERENCES `cloud_usage`.`quota_account`(`account_id`),
|
CONSTRAINT `FK_quota_email_configuration_account_id` FOREIGN KEY (`account_id`) REFERENCES `cloud_usage`.`quota_account`(`account_id`),
|
||||||
CONSTRAINT `FK_quota_email_configuration_email_template_id` FOREIGN KEY (`email_template_id`) REFERENCES `cloud_usage`.`quota_email_templates`(`id`));
|
CONSTRAINT `FK_quota_email_configuration_email_template_id` FOREIGN KEY (`email_template_id`) REFERENCES `cloud_usage`.`quota_email_templates`(`id`));
|
||||||
|
|
||||||
|
-- Add `is_implicit` column to `host_tags` table
|
||||||
|
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" ');
|
||||||
|
|||||||
@ -53,7 +53,9 @@ SELECT
|
|||||||
host_pod_ref.uuid pod_uuid,
|
host_pod_ref.uuid pod_uuid,
|
||||||
host_pod_ref.name pod_name,
|
host_pod_ref.name pod_name,
|
||||||
GROUP_CONCAT(DISTINCT(host_tags.tag)) AS tag,
|
GROUP_CONCAT(DISTINCT(host_tags.tag)) AS tag,
|
||||||
`host_tags`.`is_tag_a_rule` AS `is_tag_a_rule`,
|
GROUP_CONCAT(DISTINCT(explicit_host_tags.tag)) AS explicit_tag,
|
||||||
|
GROUP_CONCAT(DISTINCT(implicit_host_tags.tag)) AS implicit_tag,
|
||||||
|
`explicit_host_tags`.`is_tag_a_rule` AS `is_tag_a_rule`,
|
||||||
guest_os_category.id guest_os_category_id,
|
guest_os_category.id guest_os_category_id,
|
||||||
guest_os_category.uuid guest_os_category_uuid,
|
guest_os_category.uuid guest_os_category_uuid,
|
||||||
guest_os_category.name guest_os_category_name,
|
guest_os_category.name guest_os_category_name,
|
||||||
@ -89,6 +91,10 @@ FROM
|
|||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
`cloud`.`host_tags` ON host_tags.host_id = host.id
|
`cloud`.`host_tags` ON host_tags.host_id = host.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
|
`cloud`.`host_tags` AS explicit_host_tags ON explicit_host_tags.host_id = host.id AND explicit_host_tags.is_implicit = 0
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`host_tags` AS implicit_host_tags ON implicit_host_tags.host_id = host.id AND implicit_host_tags.is_implicit = 1
|
||||||
|
LEFT JOIN
|
||||||
`cloud`.`op_host_capacity` mem_caps ON host.id = mem_caps.host_id
|
`cloud`.`op_host_capacity` mem_caps ON host.id = mem_caps.host_id
|
||||||
AND mem_caps.capacity_type = 0
|
AND mem_caps.capacity_type = 0
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
|
|||||||
@ -3646,6 +3646,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||||||
cmd.setGatewayIpAddress(localGateway);
|
cmd.setGatewayIpAddress(localGateway);
|
||||||
cmd.setIqn(getIqn());
|
cmd.setIqn(getIqn());
|
||||||
cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption()));
|
cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption()));
|
||||||
|
cmd.setHostTags(getHostTags());
|
||||||
HealthCheckResult healthCheckResult = getHostHealthCheckResult();
|
HealthCheckResult healthCheckResult = getHostHealthCheckResult();
|
||||||
if (healthCheckResult != HealthCheckResult.IGNORE) {
|
if (healthCheckResult != HealthCheckResult.IGNORE) {
|
||||||
cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS);
|
cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS);
|
||||||
@ -3674,6 +3675,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||||||
return startupCommandsArray;
|
return startupCommandsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected List<String> getHostTags() {
|
||||||
|
List<String> hostTagsList = new ArrayList<>();
|
||||||
|
String hostTags = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HOST_TAGS);
|
||||||
|
if (StringUtils.isNotBlank(hostTags)) {
|
||||||
|
for (String hostTag : hostTags.split(",")) {
|
||||||
|
if (!hostTagsList.contains(hostTag.trim())) {
|
||||||
|
hostTagsList.add(hostTag.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hostTagsList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates and sets the host CPU max capacity according to the cgroup version of the host.
|
* Calculates and sets the host CPU max capacity according to the cgroup version of the host.
|
||||||
* <ul>
|
* <ul>
|
||||||
|
|||||||
@ -6295,4 +6295,40 @@ public class LibvirtComputingResourceTest {
|
|||||||
Assert.assertEquals(expectedShares, libvirtComputingResourceSpy.getHostCpuMaxCapacity());
|
Assert.assertEquals(expectedShares, libvirtComputingResourceSpy.getHostCpuMaxCapacity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetHostTags() throws ConfigurationException {
|
||||||
|
try (MockedStatic<AgentPropertiesFileHandler> ignored = Mockito.mockStatic(AgentPropertiesFileHandler.class)) {
|
||||||
|
Mockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.HOST_TAGS)))
|
||||||
|
.thenReturn("aa,bb,cc,dd");
|
||||||
|
|
||||||
|
List<String> hostTagsList = libvirtComputingResourceSpy.getHostTags();
|
||||||
|
Assert.assertEquals(4, hostTagsList.size());
|
||||||
|
Assert.assertEquals("aa,bb,cc,dd", StringUtils.join(hostTagsList, ","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetHostTagsWithSpace() throws ConfigurationException {
|
||||||
|
try (MockedStatic<AgentPropertiesFileHandler> ignored = Mockito.mockStatic(AgentPropertiesFileHandler.class)) {
|
||||||
|
Mockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.HOST_TAGS)))
|
||||||
|
.thenReturn(" aa, bb , cc , dd ");
|
||||||
|
|
||||||
|
List<String> hostTagsList = libvirtComputingResourceSpy.getHostTags();
|
||||||
|
Assert.assertEquals(4, hostTagsList.size());
|
||||||
|
Assert.assertEquals("aa,bb,cc,dd", StringUtils.join(hostTagsList, ","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetHostTagsWithEmptyPropertyValue() throws ConfigurationException {
|
||||||
|
try (MockedStatic<AgentPropertiesFileHandler> ignored = Mockito.mockStatic(AgentPropertiesFileHandler.class)) {
|
||||||
|
Mockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.HOST_TAGS)))
|
||||||
|
.thenReturn(" ");
|
||||||
|
|
||||||
|
List<String> hostTagsList = libvirtComputingResourceSpy.getHostTags();
|
||||||
|
Assert.assertEquals(0, hostTagsList.size());
|
||||||
|
Assert.assertEquals("", StringUtils.join(hostTagsList, ","));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,7 +103,6 @@ import com.cloud.api.query.dao.DiskOfferingJoinDao;
|
|||||||
import com.cloud.api.query.dao.DomainJoinDao;
|
import com.cloud.api.query.dao.DomainJoinDao;
|
||||||
import com.cloud.api.query.dao.DomainRouterJoinDao;
|
import com.cloud.api.query.dao.DomainRouterJoinDao;
|
||||||
import com.cloud.api.query.dao.HostJoinDao;
|
import com.cloud.api.query.dao.HostJoinDao;
|
||||||
import com.cloud.api.query.dao.HostTagDao;
|
|
||||||
import com.cloud.api.query.dao.ImageStoreJoinDao;
|
import com.cloud.api.query.dao.ImageStoreJoinDao;
|
||||||
import com.cloud.api.query.dao.InstanceGroupJoinDao;
|
import com.cloud.api.query.dao.InstanceGroupJoinDao;
|
||||||
import com.cloud.api.query.dao.NetworkOfferingJoinDao;
|
import com.cloud.api.query.dao.NetworkOfferingJoinDao;
|
||||||
@ -129,7 +128,6 @@ import com.cloud.api.query.vo.DomainJoinVO;
|
|||||||
import com.cloud.api.query.vo.DomainRouterJoinVO;
|
import com.cloud.api.query.vo.DomainRouterJoinVO;
|
||||||
import com.cloud.api.query.vo.EventJoinVO;
|
import com.cloud.api.query.vo.EventJoinVO;
|
||||||
import com.cloud.api.query.vo.HostJoinVO;
|
import com.cloud.api.query.vo.HostJoinVO;
|
||||||
import com.cloud.api.query.vo.HostTagVO;
|
|
||||||
import com.cloud.api.query.vo.ImageStoreJoinVO;
|
import com.cloud.api.query.vo.ImageStoreJoinVO;
|
||||||
import com.cloud.api.query.vo.InstanceGroupJoinVO;
|
import com.cloud.api.query.vo.InstanceGroupJoinVO;
|
||||||
import com.cloud.api.query.vo.NetworkOfferingJoinVO;
|
import com.cloud.api.query.vo.NetworkOfferingJoinVO;
|
||||||
@ -183,9 +181,11 @@ import com.cloud.gpu.dao.VGPUTypesDao;
|
|||||||
import com.cloud.ha.HighAvailabilityManager;
|
import com.cloud.ha.HighAvailabilityManager;
|
||||||
import com.cloud.host.Host;
|
import com.cloud.host.Host;
|
||||||
import com.cloud.host.HostStats;
|
import com.cloud.host.HostStats;
|
||||||
|
import com.cloud.host.HostTagVO;
|
||||||
import com.cloud.host.HostVO;
|
import com.cloud.host.HostVO;
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
import com.cloud.host.dao.HostDetailsDao;
|
import com.cloud.host.dao.HostDetailsDao;
|
||||||
|
import com.cloud.host.dao.HostTagsDao;
|
||||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
import com.cloud.network.IpAddress;
|
import com.cloud.network.IpAddress;
|
||||||
import com.cloud.network.Network;
|
import com.cloud.network.Network;
|
||||||
@ -452,7 +452,7 @@ public class ApiDBUtils {
|
|||||||
static VolumeJoinDao s_volJoinDao;
|
static VolumeJoinDao s_volJoinDao;
|
||||||
static StoragePoolJoinDao s_poolJoinDao;
|
static StoragePoolJoinDao s_poolJoinDao;
|
||||||
static StoragePoolTagsDao s_tagDao;
|
static StoragePoolTagsDao s_tagDao;
|
||||||
static HostTagDao s_hostTagDao;
|
static HostTagsDao s_hostTagDao;
|
||||||
static ImageStoreJoinDao s_imageStoreJoinDao;
|
static ImageStoreJoinDao s_imageStoreJoinDao;
|
||||||
static AccountJoinDao s_accountJoinDao;
|
static AccountJoinDao s_accountJoinDao;
|
||||||
static AsyncJobJoinDao s_jobJoinDao;
|
static AsyncJobJoinDao s_jobJoinDao;
|
||||||
@ -675,7 +675,7 @@ public class ApiDBUtils {
|
|||||||
@Inject
|
@Inject
|
||||||
private StoragePoolTagsDao tagDao;
|
private StoragePoolTagsDao tagDao;
|
||||||
@Inject
|
@Inject
|
||||||
private HostTagDao hosttagDao;
|
private HostTagsDao hosttagDao;
|
||||||
@Inject
|
@Inject
|
||||||
private ImageStoreJoinDao imageStoreJoinDao;
|
private ImageStoreJoinDao imageStoreJoinDao;
|
||||||
@Inject
|
@Inject
|
||||||
|
|||||||
@ -172,7 +172,6 @@ import com.cloud.api.query.dao.DiskOfferingJoinDao;
|
|||||||
import com.cloud.api.query.dao.DomainJoinDao;
|
import com.cloud.api.query.dao.DomainJoinDao;
|
||||||
import com.cloud.api.query.dao.DomainRouterJoinDao;
|
import com.cloud.api.query.dao.DomainRouterJoinDao;
|
||||||
import com.cloud.api.query.dao.HostJoinDao;
|
import com.cloud.api.query.dao.HostJoinDao;
|
||||||
import com.cloud.api.query.dao.HostTagDao;
|
|
||||||
import com.cloud.api.query.dao.ImageStoreJoinDao;
|
import com.cloud.api.query.dao.ImageStoreJoinDao;
|
||||||
import com.cloud.api.query.dao.InstanceGroupJoinDao;
|
import com.cloud.api.query.dao.InstanceGroupJoinDao;
|
||||||
import com.cloud.api.query.dao.ManagementServerJoinDao;
|
import com.cloud.api.query.dao.ManagementServerJoinDao;
|
||||||
@ -197,7 +196,6 @@ import com.cloud.api.query.vo.DomainJoinVO;
|
|||||||
import com.cloud.api.query.vo.DomainRouterJoinVO;
|
import com.cloud.api.query.vo.DomainRouterJoinVO;
|
||||||
import com.cloud.api.query.vo.EventJoinVO;
|
import com.cloud.api.query.vo.EventJoinVO;
|
||||||
import com.cloud.api.query.vo.HostJoinVO;
|
import com.cloud.api.query.vo.HostJoinVO;
|
||||||
import com.cloud.api.query.vo.HostTagVO;
|
|
||||||
import com.cloud.api.query.vo.ImageStoreJoinVO;
|
import com.cloud.api.query.vo.ImageStoreJoinVO;
|
||||||
import com.cloud.api.query.vo.InstanceGroupJoinVO;
|
import com.cloud.api.query.vo.InstanceGroupJoinVO;
|
||||||
import com.cloud.api.query.vo.ManagementServerJoinVO;
|
import com.cloud.api.query.vo.ManagementServerJoinVO;
|
||||||
@ -229,8 +227,10 @@ import com.cloud.exception.InvalidParameterValueException;
|
|||||||
import com.cloud.exception.PermissionDeniedException;
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
import com.cloud.ha.HighAvailabilityManager;
|
import com.cloud.ha.HighAvailabilityManager;
|
||||||
import com.cloud.host.Host;
|
import com.cloud.host.Host;
|
||||||
|
import com.cloud.host.HostTagVO;
|
||||||
import com.cloud.host.HostVO;
|
import com.cloud.host.HostVO;
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
|
import com.cloud.host.dao.HostTagsDao;
|
||||||
import com.cloud.hypervisor.Hypervisor;
|
import com.cloud.hypervisor.Hypervisor;
|
||||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
import com.cloud.network.PublicIpQuarantine;
|
import com.cloud.network.PublicIpQuarantine;
|
||||||
@ -426,7 +426,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
private StoragePoolTagsDao _storageTagDao;
|
private StoragePoolTagsDao _storageTagDao;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private HostTagDao _hostTagDao;
|
private HostTagsDao _hostTagDao;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ImageStoreJoinDao _imageStoreJoinDao;
|
private ImageStoreJoinDao _imageStoreJoinDao;
|
||||||
@ -2268,10 +2268,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
if (haHosts != null && haTag != null && !haTag.isEmpty()) {
|
if (haHosts != null && haTag != null && !haTag.isEmpty()) {
|
||||||
SearchBuilder<HostTagVO> hostTagSearchBuilder = _hostTagDao.createSearchBuilder();
|
SearchBuilder<HostTagVO> hostTagSearchBuilder = _hostTagDao.createSearchBuilder();
|
||||||
if ((Boolean)haHosts) {
|
if ((Boolean)haHosts) {
|
||||||
hostTagSearchBuilder.and("tag", hostTagSearchBuilder.entity().getName(), SearchCriteria.Op.EQ);
|
hostTagSearchBuilder.and("tag", hostTagSearchBuilder.entity().getTag(), SearchCriteria.Op.EQ);
|
||||||
} else {
|
} else {
|
||||||
hostTagSearchBuilder.and().op("tag", hostTagSearchBuilder.entity().getName(), Op.NEQ);
|
hostTagSearchBuilder.and().op("tag", hostTagSearchBuilder.entity().getTag(), Op.NEQ);
|
||||||
hostTagSearchBuilder.or("tagNull", hostTagSearchBuilder.entity().getName(), Op.NULL);
|
hostTagSearchBuilder.or("tagNull", hostTagSearchBuilder.entity().getTag(), Op.NULL);
|
||||||
hostTagSearchBuilder.cp();
|
hostTagSearchBuilder.cp();
|
||||||
}
|
}
|
||||||
hostSearchBuilder.join("hostTagSearch", hostTagSearchBuilder, hostSearchBuilder.entity().getId(), hostTagSearchBuilder.entity().getHostId(), JoinBuilder.JoinType.LEFT);
|
hostSearchBuilder.join("hostTagSearch", hostTagSearchBuilder, hostSearchBuilder.entity().getId(), hostTagSearchBuilder.entity().getHostId(), JoinBuilder.JoinType.LEFT);
|
||||||
|
|||||||
@ -74,7 +74,6 @@ import com.cloud.api.query.vo.DomainJoinVO;
|
|||||||
import com.cloud.api.query.vo.DomainRouterJoinVO;
|
import com.cloud.api.query.vo.DomainRouterJoinVO;
|
||||||
import com.cloud.api.query.vo.EventJoinVO;
|
import com.cloud.api.query.vo.EventJoinVO;
|
||||||
import com.cloud.api.query.vo.HostJoinVO;
|
import com.cloud.api.query.vo.HostJoinVO;
|
||||||
import com.cloud.api.query.vo.HostTagVO;
|
|
||||||
import com.cloud.api.query.vo.ImageStoreJoinVO;
|
import com.cloud.api.query.vo.ImageStoreJoinVO;
|
||||||
import com.cloud.api.query.vo.InstanceGroupJoinVO;
|
import com.cloud.api.query.vo.InstanceGroupJoinVO;
|
||||||
import com.cloud.api.query.vo.ProjectAccountJoinVO;
|
import com.cloud.api.query.vo.ProjectAccountJoinVO;
|
||||||
@ -91,6 +90,7 @@ import com.cloud.api.query.vo.UserVmJoinVO;
|
|||||||
import com.cloud.api.query.vo.VolumeJoinVO;
|
import com.cloud.api.query.vo.VolumeJoinVO;
|
||||||
import com.cloud.configuration.Resource;
|
import com.cloud.configuration.Resource;
|
||||||
import com.cloud.domain.Domain;
|
import com.cloud.domain.Domain;
|
||||||
|
import com.cloud.host.HostTagVO;
|
||||||
import com.cloud.storage.Storage.ImageFormat;
|
import com.cloud.storage.Storage.ImageFormat;
|
||||||
import com.cloud.storage.StoragePoolTagVO;
|
import com.cloud.storage.StoragePoolTagVO;
|
||||||
import com.cloud.storage.VolumeStats;
|
import com.cloud.storage.VolumeStats;
|
||||||
|
|||||||
@ -205,6 +205,8 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
|
|||||||
hostResponse.setHostTags(hostTags);
|
hostResponse.setHostTags(hostTags);
|
||||||
hostResponse.setIsTagARule(host.getIsTagARule());
|
hostResponse.setIsTagARule(host.getIsTagARule());
|
||||||
hostResponse.setHaHost(containsHostHATag(hostTags));
|
hostResponse.setHaHost(containsHostHATag(hostTags));
|
||||||
|
hostResponse.setExplicitHostTags(host.getExplicitTag());
|
||||||
|
hostResponse.setImplicitHostTags(host.getImplicitTag());
|
||||||
|
|
||||||
hostResponse.setHypervisorVersion(host.getHypervisorVersion());
|
hostResponse.setHypervisorVersion(host.getHypervisorVersion());
|
||||||
|
|
||||||
@ -349,6 +351,7 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
|
|||||||
String hostTags = host.getTag();
|
String hostTags = host.getTag();
|
||||||
hostResponse.setHostTags(hostTags);
|
hostResponse.setHostTags(hostTags);
|
||||||
hostResponse.setHaHost(containsHostHATag(hostTags));
|
hostResponse.setHaHost(containsHostHATag(hostTags));
|
||||||
|
hostResponse.setImplicitHostTags(host.getImplicitTag());
|
||||||
|
|
||||||
hostResponse.setHypervisorVersion(host.getHypervisorVersion());
|
hostResponse.setHypervisorVersion(host.getHypervisorVersion());
|
||||||
|
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
// Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
// or more contributor license agreements. See the NOTICE file
|
|
||||||
// distributed with this work for additional information
|
|
||||||
// regarding copyright ownership. The ASF licenses this file
|
|
||||||
// to you under the Apache License, Version 2.0 (the
|
|
||||||
// "License"); you may not use this file except in compliance
|
|
||||||
// with the License. You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing,
|
|
||||||
// software distributed under the License is distributed on an
|
|
||||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
// KIND, either express or implied. See the License for the
|
|
||||||
// specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
package com.cloud.api.query.dao;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.api.response.HostTagResponse;
|
|
||||||
|
|
||||||
import com.cloud.api.query.vo.HostTagVO;
|
|
||||||
import com.cloud.utils.db.GenericDao;
|
|
||||||
|
|
||||||
public interface HostTagDao extends GenericDao<HostTagVO, Long> {
|
|
||||||
HostTagResponse newHostTagResponse(HostTagVO hostTag);
|
|
||||||
|
|
||||||
List<HostTagVO> searchByIds(Long... hostTagIds);
|
|
||||||
}
|
|
||||||
@ -1,122 +0,0 @@
|
|||||||
// Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
// or more contributor license agreements. See the NOTICE file
|
|
||||||
// distributed with this work for additional information
|
|
||||||
// regarding copyright ownership. The ASF licenses this file
|
|
||||||
// to you under the Apache License, Version 2.0 (the
|
|
||||||
// "License"); you may not use this file except in compliance
|
|
||||||
// with the License. You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing,
|
|
||||||
// software distributed under the License is distributed on an
|
|
||||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
// KIND, either express or implied. See the License for the
|
|
||||||
// specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
package com.cloud.api.query.dao;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.api.response.HostTagResponse;
|
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import com.cloud.api.query.vo.HostTagVO;
|
|
||||||
import com.cloud.utils.db.GenericDaoBase;
|
|
||||||
import com.cloud.utils.db.SearchBuilder;
|
|
||||||
import com.cloud.utils.db.SearchCriteria;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class HostTagDaoImpl extends GenericDaoBase<HostTagVO, Long> implements HostTagDao {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private ConfigurationDao _configDao;
|
|
||||||
|
|
||||||
private final SearchBuilder<HostTagVO> stSearch;
|
|
||||||
private final SearchBuilder<HostTagVO> stIdSearch;
|
|
||||||
|
|
||||||
protected HostTagDaoImpl() {
|
|
||||||
stSearch = createSearchBuilder();
|
|
||||||
|
|
||||||
stSearch.and("idIN", stSearch.entity().getId(), SearchCriteria.Op.IN);
|
|
||||||
stSearch.done();
|
|
||||||
|
|
||||||
stIdSearch = createSearchBuilder();
|
|
||||||
|
|
||||||
stIdSearch.and("id", stIdSearch.entity().getId(), SearchCriteria.Op.EQ);
|
|
||||||
stIdSearch.done();
|
|
||||||
|
|
||||||
_count = "select count(distinct id) from host_tags WHERE ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HostTagResponse newHostTagResponse(HostTagVO tag) {
|
|
||||||
HostTagResponse tagResponse = new HostTagResponse();
|
|
||||||
|
|
||||||
tagResponse.setName(tag.getName());
|
|
||||||
tagResponse.setHostId(tag.getHostId());
|
|
||||||
|
|
||||||
tagResponse.setObjectName("hosttag");
|
|
||||||
|
|
||||||
return tagResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<HostTagVO> searchByIds(Long... stIds) {
|
|
||||||
String batchCfg = _configDao.getValue("detail.batch.query.size");
|
|
||||||
|
|
||||||
final int detailsBatchSize = batchCfg != null ? Integer.parseInt(batchCfg) : 2000;
|
|
||||||
|
|
||||||
// query details by batches
|
|
||||||
List<HostTagVO> uvList = new ArrayList<HostTagVO>();
|
|
||||||
int curr_index = 0;
|
|
||||||
|
|
||||||
if (stIds.length > detailsBatchSize) {
|
|
||||||
while ((curr_index + detailsBatchSize) <= stIds.length) {
|
|
||||||
Long[] ids = new Long[detailsBatchSize];
|
|
||||||
|
|
||||||
for (int k = 0, j = curr_index; j < curr_index + detailsBatchSize; j++, k++) {
|
|
||||||
ids[k] = stIds[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchCriteria<HostTagVO> sc = stSearch.create();
|
|
||||||
|
|
||||||
sc.setParameters("idIN", (Object[])ids);
|
|
||||||
|
|
||||||
List<HostTagVO> vms = searchIncludingRemoved(sc, null, null, false);
|
|
||||||
|
|
||||||
if (vms != null) {
|
|
||||||
uvList.addAll(vms);
|
|
||||||
}
|
|
||||||
|
|
||||||
curr_index += detailsBatchSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curr_index < stIds.length) {
|
|
||||||
int batch_size = (stIds.length - curr_index);
|
|
||||||
// set the ids value
|
|
||||||
Long[] ids = new Long[batch_size];
|
|
||||||
|
|
||||||
for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) {
|
|
||||||
ids[k] = stIds[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchCriteria<HostTagVO> sc = stSearch.create();
|
|
||||||
|
|
||||||
sc.setParameters("idIN", (Object[])ids);
|
|
||||||
|
|
||||||
List<HostTagVO> vms = searchIncludingRemoved(sc, null, null, false);
|
|
||||||
|
|
||||||
if (vms != null) {
|
|
||||||
uvList.addAll(vms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return uvList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -174,6 +174,12 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
|
|||||||
@Column(name = "tag")
|
@Column(name = "tag")
|
||||||
private String tag;
|
private String tag;
|
||||||
|
|
||||||
|
@Column(name = "explicit_tag")
|
||||||
|
private String explicitTag;
|
||||||
|
|
||||||
|
@Column(name = "implicit_tag")
|
||||||
|
private String implicitTag;
|
||||||
|
|
||||||
@Column(name = "is_tag_a_rule")
|
@Column(name = "is_tag_a_rule")
|
||||||
private Boolean isTagARule;
|
private Boolean isTagARule;
|
||||||
|
|
||||||
@ -393,6 +399,14 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
|
|||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getExplicitTag() {
|
||||||
|
return explicitTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImplicitTag() {
|
||||||
|
return implicitTag;
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getIsTagARule() {
|
public Boolean getIsTagARule() {
|
||||||
return isTagARule;
|
return isTagARule;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,61 +0,0 @@
|
|||||||
// Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
// or more contributor license agreements. See the NOTICE file
|
|
||||||
// distributed with this work for additional information
|
|
||||||
// regarding copyright ownership. The ASF licenses this file
|
|
||||||
// to you under the Apache License, Version 2.0 (the
|
|
||||||
// "License"); you may not use this file except in compliance
|
|
||||||
// with the License. You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing,
|
|
||||||
// software distributed under the License is distributed on an
|
|
||||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
// KIND, either express or implied. See the License for the
|
|
||||||
// specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
package com.cloud.api.query.vo;
|
|
||||||
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.api.InternalIdentity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Storage Tags DB view.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Entity
|
|
||||||
@Table(name = "host_tags")
|
|
||||||
public class HostTagVO extends BaseViewVO implements InternalIdentity {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@Column(name = "id")
|
|
||||||
private long id;
|
|
||||||
|
|
||||||
@Column(name = "tag")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Column(name = "host_id")
|
|
||||||
long hostId;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getHostId() {
|
|
||||||
return hostId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHostId(long hostId) {
|
|
||||||
this.hostId = hostId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -38,9 +38,9 @@ import javax.inject.Inject;
|
|||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
import com.cloud.alert.AlertManager;
|
import com.cloud.alert.AlertManager;
|
||||||
import com.cloud.host.HostTagVO;
|
|
||||||
import com.cloud.exception.StorageConflictException;
|
import com.cloud.exception.StorageConflictException;
|
||||||
import com.cloud.exception.StorageUnavailableException;
|
import com.cloud.exception.StorageUnavailableException;
|
||||||
|
import com.cloud.host.HostTagVO;
|
||||||
import com.cloud.storage.Volume;
|
import com.cloud.storage.Volume;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.storage.dao.VolumeDao;
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
@ -2334,22 +2334,6 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startup instanceof StartupRoutingCommand) {
|
|
||||||
final StartupRoutingCommand ssCmd = (StartupRoutingCommand)startup;
|
|
||||||
final List<String> implicitHostTags = ssCmd.getHostTags();
|
|
||||||
if (!implicitHostTags.isEmpty()) {
|
|
||||||
if (hostTags == null) {
|
|
||||||
hostTags = _hostTagsDao.getHostTags(host.getId()).parallelStream().map(HostTagVO::getTag).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
if (hostTags != null) {
|
|
||||||
implicitHostTags.removeAll(hostTags);
|
|
||||||
hostTags.addAll(implicitHostTags);
|
|
||||||
} else {
|
|
||||||
hostTags = implicitHostTags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
host.setDataCenterId(dc.getId());
|
host.setDataCenterId(dc.getId());
|
||||||
host.setPodId(podId);
|
host.setPodId(podId);
|
||||||
host.setClusterId(clusterId);
|
host.setClusterId(clusterId);
|
||||||
@ -2392,6 +2376,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
|
|
||||||
if (startup instanceof StartupRoutingCommand) {
|
if (startup instanceof StartupRoutingCommand) {
|
||||||
final StartupRoutingCommand ssCmd = (StartupRoutingCommand)startup;
|
final StartupRoutingCommand ssCmd = (StartupRoutingCommand)startup;
|
||||||
|
_hostTagsDao.updateImplicitTags(host.getId(), ssCmd.getHostTags());
|
||||||
|
|
||||||
updateSupportsClonedVolumes(host, ssCmd.getSupportsClonedVolumes());
|
updateSupportsClonedVolumes(host, ssCmd.getSupportsClonedVolumes());
|
||||||
}
|
}
|
||||||
|
|||||||
160
test/integration/smoke/test_host_tags.py
Normal file
160
test/integration/smoke/test_host_tags.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
""" Tests for importVolume and unmanageVolume APIs
|
||||||
|
"""
|
||||||
|
# Import Local Modules
|
||||||
|
from marvin.cloudstackAPI import updateHost
|
||||||
|
from marvin.cloudstackTestCase import cloudstackTestCase, unittest
|
||||||
|
from marvin.lib.base import Host
|
||||||
|
from marvin.lib.utils import is_server_ssh_ready, wait_until
|
||||||
|
|
||||||
|
# Import System modules
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class TestHostTags(cloudstackTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
testClient = super(TestHostTags, cls).getClsTestClient()
|
||||||
|
cls.apiclient = testClient.getApiClient()
|
||||||
|
cls.hypervisor = testClient.getHypervisorInfo()
|
||||||
|
if cls.testClient.getHypervisorInfo().lower() != "kvm":
|
||||||
|
raise unittest.SkipTest("This is only available for KVM")
|
||||||
|
|
||||||
|
cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__
|
||||||
|
|
||||||
|
hosts = Host.list(
|
||||||
|
cls.apiclient,
|
||||||
|
type = "Routing",
|
||||||
|
hypervisor = cls.hypervisor
|
||||||
|
)
|
||||||
|
if isinstance(hosts, list) and len(hosts) > 0:
|
||||||
|
cls.host = hosts[0]
|
||||||
|
else:
|
||||||
|
raise unittest.SkipTest("No available host for this test")
|
||||||
|
|
||||||
|
cls.logger = logging.getLogger("TestHostTags")
|
||||||
|
cls.stream_handler = logging.StreamHandler()
|
||||||
|
cls.logger.setLevel(logging.DEBUG)
|
||||||
|
cls.logger.addHandler(cls.stream_handler)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.update_host_tags_via_api(cls.host.hosttags)
|
||||||
|
cls.update_implicit_host_tags_via_agent_properties(cls.host.implicithosttags)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_host_tags_via_api(cls, hosttags):
|
||||||
|
cmd = updateHost.updateHostCmd()
|
||||||
|
cmd.id = cls.host.id
|
||||||
|
cmd.hosttags = hosttags
|
||||||
|
cls.apiclient.updateHost(cmd)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_implicit_host_tags_via_agent_properties(cls, implicithosttags):
|
||||||
|
ssh_client = is_server_ssh_ready(
|
||||||
|
cls.host.ipaddress,
|
||||||
|
22,
|
||||||
|
cls.hostConfig["username"],
|
||||||
|
cls.hostConfig["password"],
|
||||||
|
)
|
||||||
|
if implicithosttags:
|
||||||
|
command = "sed -i '/host.tags=/d' /etc/cloudstack/agent/agent.properties \
|
||||||
|
&& echo 'host.tags=%s' >> /etc/cloudstack/agent/agent.properties \
|
||||||
|
&& systemctl restart cloudstack-agent" % implicithosttags
|
||||||
|
else:
|
||||||
|
command = "sed -i '/host.tags=/d' /etc/cloudstack/agent/agent.properties \
|
||||||
|
&& systemctl restart cloudstack-agent"
|
||||||
|
|
||||||
|
ssh_client.execute(command)
|
||||||
|
|
||||||
|
def wait_until_host_is_up_and_verify_hosttags(self, explicithosttags, implicithosttags, interval=3, retries=20):
|
||||||
|
def check_host_state():
|
||||||
|
hosts = Host.list(
|
||||||
|
self.apiclient,
|
||||||
|
id=self.host.id
|
||||||
|
)
|
||||||
|
if isinstance(hosts, list) and len(hosts) > 0:
|
||||||
|
host = hosts[0]
|
||||||
|
if host.state == "Up":
|
||||||
|
self.logger.debug("Host %s is in Up state" % host.name)
|
||||||
|
self.logger.debug("Host explicithosttags is %s, implicit hosttags is %s" % (host.explicithosttags, host.implicithosttags))
|
||||||
|
if explicithosttags:
|
||||||
|
self.assertEquals(explicithosttags, host.explicithosttags)
|
||||||
|
else:
|
||||||
|
self.assertIsNone(host.explicithosttags)
|
||||||
|
if implicithosttags:
|
||||||
|
self.assertEquals(implicithosttags, host.implicithosttags)
|
||||||
|
else:
|
||||||
|
self.assertIsNone(host.implicithosttags)
|
||||||
|
return True, None
|
||||||
|
else:
|
||||||
|
self.logger.debug("Waiting for host %s to be Up state, current state is %s" % (host.name, host.state))
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
done, _ = wait_until(interval, retries, check_host_state)
|
||||||
|
if not done:
|
||||||
|
raise Exception("Failed to wait for host %s to be Up" % self.host.name)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@attr(tags=['advanced', 'basic', 'sg'], required_hardware=False)
|
||||||
|
def test_01_host_tags(self):
|
||||||
|
"""Test implicit/explicit host tags
|
||||||
|
"""
|
||||||
|
|
||||||
|
# update explicit host tags to "s1,s2"
|
||||||
|
explicithosttags="s1,s2"
|
||||||
|
implicithosttags=self.host.implicithosttags
|
||||||
|
self.update_host_tags_via_api(explicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
|
|
||||||
|
# update implicit host tags to "d1,d2"
|
||||||
|
implicithosttags="d1,d2"
|
||||||
|
self.update_implicit_host_tags_via_agent_properties(implicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
|
|
||||||
|
# update explicit host tags to "s3,s4"
|
||||||
|
explicithosttags="s3,s4"
|
||||||
|
self.update_host_tags_via_api(explicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
|
|
||||||
|
# update implicit host tags to "d3,d4"
|
||||||
|
implicithosttags="d3,d4"
|
||||||
|
self.update_implicit_host_tags_via_agent_properties(implicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
|
|
||||||
|
# update hosttags to ""
|
||||||
|
explicithosttags=""
|
||||||
|
self.update_host_tags_via_api(explicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
|
|
||||||
|
# update implicit host tags to ""
|
||||||
|
implicithosttags=""
|
||||||
|
self.update_implicit_host_tags_via_agent_properties(implicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
|
|
||||||
|
# update explicit host tags to "s1,s2"
|
||||||
|
explicithosttags="s1,s2"
|
||||||
|
self.update_host_tags_via_api(explicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
|
|
||||||
|
# update implicit host tags to "d1,d2"
|
||||||
|
implicithosttags="d1,d2"
|
||||||
|
self.update_implicit_host_tags_via_agent_properties(implicithosttags)
|
||||||
|
self.wait_until_host_is_up_and_verify_hosttags(explicithosttags, implicithosttags)
|
||||||
@ -198,6 +198,7 @@
|
|||||||
"label.action.unmanage.virtualmachine": "Unmanage Instance",
|
"label.action.unmanage.virtualmachine": "Unmanage Instance",
|
||||||
"label.action.unmanage.volume": "Unmanage Volume",
|
"label.action.unmanage.volume": "Unmanage Volume",
|
||||||
"label.action.unmanage.volumes": "Unmanage Volumes",
|
"label.action.unmanage.volumes": "Unmanage Volumes",
|
||||||
|
"label.action.update.host": "Update host",
|
||||||
"label.action.update.offering.access": "Update offering access",
|
"label.action.update.offering.access": "Update offering access",
|
||||||
"label.action.update.resource.count": "Update resource count",
|
"label.action.update.resource.count": "Update resource count",
|
||||||
"label.action.value": "Action/Value",
|
"label.action.value": "Action/Value",
|
||||||
@ -1006,6 +1007,12 @@
|
|||||||
"label.hostnamelabel": "Host name",
|
"label.hostnamelabel": "Host name",
|
||||||
"label.hosts": "Hosts",
|
"label.hosts": "Hosts",
|
||||||
"label.hosttags": "Host tags",
|
"label.hosttags": "Host tags",
|
||||||
|
"label.hosttags.explicit": "API-defined Host tags",
|
||||||
|
"label.hosttags.explicit.abbr": "api-defined",
|
||||||
|
"label.hosttags.explicit.description": "The host tags defined by CloudStack APIs",
|
||||||
|
"label.hosttags.implicit": "Agent-defined Host tags",
|
||||||
|
"label.hosttags.implicit.abbr": "agent-defined",
|
||||||
|
"label.hosttags.implicit.description": "The host tags defined by CloudStack Agent",
|
||||||
"label.hourly": "Hourly",
|
"label.hourly": "Hourly",
|
||||||
"label.hypervisor": "Hypervisor",
|
"label.hypervisor": "Hypervisor",
|
||||||
"label.hypervisor.capabilities": "Hypervisor capabilities",
|
"label.hypervisor.capabilities": "Hypervisor capabilities",
|
||||||
|
|||||||
@ -74,12 +74,8 @@ export default {
|
|||||||
icon: 'edit-outlined',
|
icon: 'edit-outlined',
|
||||||
label: 'label.edit',
|
label: 'label.edit',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['name', 'hosttags', 'istagarule', 'oscategoryid'],
|
popup: true,
|
||||||
mapping: {
|
component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostUpdate')))
|
||||||
oscategoryid: {
|
|
||||||
api: 'listOsCategories'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'provisionCertificate',
|
api: 'provisionCertificate',
|
||||||
|
|||||||
@ -51,8 +51,14 @@
|
|||||||
<a-list-item v-if="host.hosttags">
|
<a-list-item v-if="host.hosttags">
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ $t('label.hosttags') }}</strong>
|
<strong>{{ $t('label.hosttags') }}</strong>
|
||||||
<div>
|
<div v-for="hosttag in host.allhosttags" :key="hosttag.tag">
|
||||||
{{ host.hosttags }}
|
{{ hosttag.tag }}
|
||||||
|
<span v-if="hosttag.isexplicit">
|
||||||
|
<a-tag color="blue">{{ $t('label.hosttags.explicit.abbr') }}</a-tag>
|
||||||
|
</span>
|
||||||
|
<span v-if="hosttag.isimplicit">
|
||||||
|
<a-tag color="orange">{{ $t('label.hosttags.implicit.abbr') }}</a-tag>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
@ -158,6 +164,22 @@ export default {
|
|||||||
this.fetchLoading = true
|
this.fetchLoading = true
|
||||||
api('listHosts', { id: this.resource.id }).then(json => {
|
api('listHosts', { id: this.resource.id }).then(json => {
|
||||||
this.host = json.listhostsresponse.host[0]
|
this.host = json.listhostsresponse.host[0]
|
||||||
|
const hosttags = this.host.hosttags?.split(',') || []
|
||||||
|
const explicithosttags = this.host.explicithosttags?.split(',') || []
|
||||||
|
const implicithosttags = this.host.implicithosttags?.split(',') || []
|
||||||
|
const allHostTags = []
|
||||||
|
for (const hosttag of hosttags) {
|
||||||
|
var isexplicit = false
|
||||||
|
var isimplicit = false
|
||||||
|
if (explicithosttags.includes(hosttag)) {
|
||||||
|
isexplicit = true
|
||||||
|
}
|
||||||
|
if (implicithosttags.includes(hosttag)) {
|
||||||
|
isimplicit = true
|
||||||
|
}
|
||||||
|
allHostTags.push({ tag: hosttag, isexplicit: isexplicit, isimplicit: isimplicit })
|
||||||
|
}
|
||||||
|
this.host.allhosttags = allHostTags
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.$notifyError(error)
|
this.$notifyError(error)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
|||||||
183
ui/src/views/infra/HostUpdate.vue
Normal file
183
ui/src/views/infra/HostUpdate.vue
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-form
|
||||||
|
class="form-layout"
|
||||||
|
layout="vertical"
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
v-ctrl-enter="handleSubmit"
|
||||||
|
@finish="handleSubmit">
|
||||||
|
<a-form-item name="name" ref="name">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.name"
|
||||||
|
v-focus="true" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="hosttags" ref="hosttags">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.hosttags')" :tooltip="$t('label.hosttags.explicit.description')"/>
|
||||||
|
</template>
|
||||||
|
<a-input v-model:value="form.hosttags" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="istagarule" ref="istagarule">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.istagarule')" :tooltip="apiParams.istagarule.description"/>
|
||||||
|
</template>
|
||||||
|
<a-switch v-model:checked="form.istagarule" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="oscategoryid" ref="oscategoryid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.oscategoryid')" :tooltip="apiParams.oscategoryid.description"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}"
|
||||||
|
:loading="osCategories.loading"
|
||||||
|
v-model:value="form.oscategoryid">
|
||||||
|
<a-select-option v-for="(osCategory) in osCategories.opts" :key="osCategory.id" :label="osCategory.name">
|
||||||
|
{{ osCategory.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button :loading="loading" @click="onCloseAction">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EditVM',
|
||||||
|
components: {
|
||||||
|
TooltipLabel
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
action: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
osCategories: {
|
||||||
|
loading: false,
|
||||||
|
opts: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('updateHost')
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.initForm()
|
||||||
|
this.fetchOsCategories()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({
|
||||||
|
name: this.resource.name,
|
||||||
|
hosttags: this.resource.explicithosttags,
|
||||||
|
istagarule: this.resource.istagarule,
|
||||||
|
oscategoryid: this.resource.oscategoryid
|
||||||
|
})
|
||||||
|
this.rules = reactive({})
|
||||||
|
},
|
||||||
|
fetchOsCategories () {
|
||||||
|
this.osCategories.loading = true
|
||||||
|
this.osCategories.opts = []
|
||||||
|
api('listOsCategories').then(json => {
|
||||||
|
this.osCategories.opts = json.listoscategoriesresponse.oscategory || []
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.osCategories.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSubmit () {
|
||||||
|
this.formRef.value.validate().then(() => {
|
||||||
|
const values = toRaw(this.form)
|
||||||
|
const params = {}
|
||||||
|
params.id = this.resource.id
|
||||||
|
params.name = values.name
|
||||||
|
params.hosttags = values.hosttags
|
||||||
|
params.oscategoryid = values.oscategoryid
|
||||||
|
if (values.istagarule !== undefined) {
|
||||||
|
params.istagarule = values.istagarule
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
api('updateHost', params).then(json => {
|
||||||
|
this.$message.success({
|
||||||
|
content: `${this.$t('label.action.update.host')} - ${values.name}`,
|
||||||
|
duration: 2
|
||||||
|
})
|
||||||
|
this.$emit('refresh-data')
|
||||||
|
this.onCloseAction()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => { this.loading = false })
|
||||||
|
}).catch(error => {
|
||||||
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCloseAction () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.form-layout {
|
||||||
|
width: 80vw;
|
||||||
|
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user