Skip to content

Commit 664a46a

Browse files
DK101010Dirk Klahre
andauthored
PR multi tags in compute offering [#4398] (#4399)
* [#4398] adapt code to handle multi tag string with commas * [#4398] remove trailing spaces * [#4398] add multi host tag support for ingest process * [#4398] add test for multi tag support in offerings * [#4398] update multitag support for DeploymentPlanningManagerImpl encapsulate multi tag check from Ingest Feature, DepolymentPlanningManager into HostDaoImpl to prevent code duplicates * [#4398] move logic to HostVO and add tests * rename test method * [#4398] Change string method to apaches StringUtils * [#4398] modify test for multi tag support * adapt sql for double tags Co-authored-by: Dirk Klahre <[email protected]>
1 parent 0838d79 commit 664a46a

File tree

7 files changed

+365
-28
lines changed

7 files changed

+365
-28
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,13 @@
4040

4141
import com.cloud.agent.api.VgpuTypesInfo;
4242
import com.cloud.hypervisor.Hypervisor.HypervisorType;
43+
import com.cloud.offering.ServiceOffering;
4344
import com.cloud.resource.ResourceState;
4445
import com.cloud.storage.Storage.StoragePoolType;
4546
import com.cloud.utils.NumbersUtil;
4647
import com.cloud.utils.db.GenericDao;
48+
import java.util.Arrays;
49+
import org.apache.commons.lang.StringUtils;
4750

4851
@Entity
4952
@Table(name = "host")
@@ -740,6 +743,18 @@ public void setUuid(String uuid) {
740743
this.uuid = uuid;
741744
}
742745

746+
public boolean checkHostServiceOfferingTags(ServiceOffering serviceOffering){
747+
if (serviceOffering == null) {
748+
return false;
749+
}
750+
if (StringUtils.isEmpty(serviceOffering.getHostTag())) {
751+
return true;
752+
}
753+
754+
List<String> serviceOfferingTags = Arrays.asList(serviceOffering.getHostTag().split(","));
755+
return this.getHostTags() != null && this.getHostTags().containsAll(serviceOfferingTags);
756+
}
757+
743758
@Override
744759
public PartitionType partitionType() {
745760
return PartitionType.Host;

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

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import com.cloud.utils.db.TransactionLegacy;
7171
import com.cloud.utils.db.UpdateBuilder;
7272
import com.cloud.utils.exception.CloudRuntimeException;
73+
import java.util.Arrays;
7374

7475
@DB
7576
@TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", valueColumnName = "sequence", allocationSize = 1)
@@ -78,12 +79,19 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
7879
private static final Logger status_logger = Logger.getLogger(Status.class);
7980
private static final Logger state_logger = Logger.getLogger(ResourceState.class);
8081

82+
private static final String LIST_HOST_IDS_BY_COMPUTETAGS = "SELECT filtered.host_id, COUNT(filtered.tag) AS tag_count "
83+
+ "FROM (SELECT host_id, tag FROM host_tags GROUP BY host_id,tag) AS filtered "
84+
+ "WHERE tag IN(%s) "
85+
+ "GROUP BY host_id "
86+
+ "HAVING tag_count = %s ";
87+
private static final String SEPARATOR = ",";
8188
private static final String LIST_CLUSTERID_FOR_HOST_TAG = "select distinct cluster_id from host join host_tags on host.id = host_tags.host_id and host_tags.tag = ?";
8289
private static final String GET_HOSTS_OF_ACTIVE_VMS = "select h.id " +
8390
"from vm_instance vm " +
8491
"join host h on (vm.host_id=h.id) " +
8592
"where vm.service_offering_id= ? and vm.state not in (\"Destroyed\", \"Expunging\", \"Error\") group by h.id";
8693

94+
8795
protected SearchBuilder<HostVO> TypePodDcStatusSearch;
8896

8997
protected SearchBuilder<HostVO> IdStatusSearch;
@@ -736,11 +744,6 @@ public void markHostsAsDisconnected(long msId, long lastPing) {
736744

737745
@Override
738746
public List<HostVO> listByHostTag(Host.Type type, Long clusterId, Long podId, long dcId, String hostTag) {
739-
740-
SearchBuilder<HostTagVO> hostTagSearch = _hostTagsDao.createSearchBuilder();
741-
HostTagVO tagEntity = hostTagSearch.entity();
742-
hostTagSearch.and("tag", tagEntity.getTag(), SearchCriteria.Op.EQ);
743-
744747
SearchBuilder<HostVO> hostSearch = createSearchBuilder();
745748
HostVO entity = hostSearch.entity();
746749
hostSearch.and("type", entity.getType(), SearchCriteria.Op.EQ);
@@ -749,10 +752,8 @@ public List<HostVO> listByHostTag(Host.Type type, Long clusterId, Long podId, lo
749752
hostSearch.and("cluster", entity.getClusterId(), SearchCriteria.Op.EQ);
750753
hostSearch.and("status", entity.getStatus(), SearchCriteria.Op.EQ);
751754
hostSearch.and("resourceState", entity.getResourceState(), SearchCriteria.Op.EQ);
752-
hostSearch.join("hostTagSearch", hostTagSearch, entity.getId(), tagEntity.getHostId(), JoinBuilder.JoinType.INNER);
753755

754756
SearchCriteria<HostVO> sc = hostSearch.create();
755-
sc.setJoinParameters("hostTagSearch", "tag", hostTag);
756757
sc.setParameters("type", type.toString());
757758
if (podId != null) {
758759
sc.setParameters("pod", podId);
@@ -764,7 +765,13 @@ public List<HostVO> listByHostTag(Host.Type type, Long clusterId, Long podId, lo
764765
sc.setParameters("status", Status.Up.toString());
765766
sc.setParameters("resourceState", ResourceState.Enabled.toString());
766767

767-
return listBy(sc);
768+
List<HostVO> tmpHosts = listBy(sc);
769+
List<HostVO> correctHostsByHostTags = new ArrayList();
770+
List<Long> hostIdsByComputeOffTags = findHostByComputeOfferings(hostTag);
771+
772+
tmpHosts.forEach((host) -> { if(hostIdsByComputeOffTags.contains(host.getId())) correctHostsByHostTags.add(host);});
773+
774+
return correctHostsByHostTags;
768775
}
769776

770777
@Override
@@ -1179,28 +1186,69 @@ public List<HostVO> listAllHostsByZoneAndHypervisorType(long zoneId, HypervisorT
11791186
}
11801187

11811188
@Override
1182-
public List<Long> listClustersByHostTag(String hostTagOnOffering) {
1189+
public List<Long> listClustersByHostTag(String computeOfferingTags) {
11831190
TransactionLegacy txn = TransactionLegacy.currentTxn();
1191+
String sql = this.LIST_CLUSTERID_FOR_HOST_TAG;
11841192
PreparedStatement pstmt = null;
1185-
List<Long> result = new ArrayList<Long>();
1186-
StringBuilder sql = new StringBuilder(LIST_CLUSTERID_FOR_HOST_TAG);
1187-
// during listing the clusters that cross the threshold
1188-
// we need to check with disabled thresholds of each cluster if not defined at cluster consider the global value
1193+
List<Long> result = new ArrayList();
1194+
List<String> tags = Arrays.asList(computeOfferingTags.split(this.SEPARATOR));
1195+
String subselect = getHostIdsByComputeTags(tags);
1196+
sql = String.format(sql, subselect);
1197+
11891198
try {
1190-
pstmt = txn.prepareAutoCloseStatement(sql.toString());
1191-
pstmt.setString(1, hostTagOnOffering);
1199+
pstmt = txn.prepareStatement(sql);
1200+
1201+
for(int i = 0; i < tags.size(); i++){
1202+
pstmt.setString(i+1, tags.get(i));
1203+
}
1204+
11921205
ResultSet rs = pstmt.executeQuery();
11931206
while (rs.next()) {
11941207
result.add(rs.getLong(1));
11951208
}
1209+
pstmt.close();
1210+
if(result.isEmpty()){
1211+
throw new CloudRuntimeException("No suitable host found for follow compute offering tags: " + computeOfferingTags);
1212+
}
11961213
return result;
11971214
} catch (SQLException e) {
11981215
throw new CloudRuntimeException("DB Exception on: " + sql, e);
1199-
} catch (Throwable e) {
1200-
throw new CloudRuntimeException("Caught: " + sql, e);
12011216
}
12021217
}
12031218

1219+
private List<Long> findHostByComputeOfferings(String computeOfferingTags){
1220+
TransactionLegacy txn = TransactionLegacy.currentTxn();
1221+
PreparedStatement pstmt = null;
1222+
List<Long> result = new ArrayList();
1223+
List<String> tags = Arrays.asList(computeOfferingTags.split(this.SEPARATOR));
1224+
String select = getHostIdsByComputeTags(tags);
1225+
try {
1226+
pstmt = txn.prepareStatement(select);
1227+
1228+
for(int i = 0; i < tags.size(); i++){
1229+
pstmt.setString(i+1, tags.get(i));
1230+
}
1231+
1232+
ResultSet rs = pstmt.executeQuery();
1233+
while (rs.next()) {
1234+
result.add(rs.getLong(1));
1235+
}
1236+
pstmt.close();
1237+
if(result.isEmpty()){
1238+
throw new CloudRuntimeException("No suitable host found for follow compute offering tags: " + computeOfferingTags);
1239+
}
1240+
return result;
1241+
} catch (SQLException e) {
1242+
throw new CloudRuntimeException("DB Exception on: " + select, e);
1243+
}
1244+
}
1245+
1246+
private String getHostIdsByComputeTags(List<String> offeringTags){
1247+
List<String> questionMarks = new ArrayList();
1248+
offeringTags.forEach((tag) -> { questionMarks.add("?"); });
1249+
return String.format(this.LIST_HOST_IDS_BY_COMPUTETAGS, String.join(",", questionMarks),questionMarks.size());
1250+
}
1251+
12041252
@Override
12051253
public List<HostVO> listHostsWithActiveVMs(long offeringId) {
12061254
TransactionLegacy txn = TransactionLegacy.currentTxn();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.host;
18+
19+
import com.cloud.service.ServiceOfferingVO;
20+
import com.cloud.storage.Storage.ProvisioningType;
21+
import com.cloud.vm.VirtualMachine;
22+
import java.util.Arrays;
23+
import static org.junit.Assert.assertFalse;
24+
import static org.junit.Assert.assertTrue;
25+
import org.junit.Test;
26+
import org.junit.Before;
27+
28+
public class HostVOTest {
29+
HostVO host;
30+
ServiceOfferingVO offering;
31+
32+
@Before
33+
public void setUp() throws Exception {
34+
host = new HostVO();
35+
offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0, false, "TestSO", ProvisioningType.THIN, true, true,"",false,VirtualMachine.Type.User,false);
36+
}
37+
38+
@Test
39+
public void testNoSO() {
40+
assertFalse(host.checkHostServiceOfferingTags(null));
41+
}
42+
43+
@Test
44+
public void testNoTag() {
45+
assertTrue(host.checkHostServiceOfferingTags(offering));
46+
}
47+
48+
@Test
49+
public void testRightTag() {
50+
host.setHostTags(Arrays.asList("tag1","tag2"));
51+
offering.setHostTag("tag2,tag1");
52+
assertTrue(host.checkHostServiceOfferingTags(offering));
53+
}
54+
55+
@Test
56+
public void testWrongTag() {
57+
host.setHostTags(Arrays.asList("tag1","tag2"));
58+
offering.setHostTag("tag2,tag4");
59+
assertFalse(host.checkHostServiceOfferingTags(offering));
60+
}
61+
}

server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ private boolean checkVmProfileAndHost(final VirtualMachineProfile vmProfile, fin
678678
ServiceOffering offering = vmProfile.getServiceOffering();
679679
if (offering.getHostTag() != null) {
680680
_hostDao.loadHostTags(host);
681-
if (!(host.getHostTags() != null && host.getHostTags().contains(offering.getHostTag()))) {
681+
if (!host.checkHostServiceOfferingTags(offering)) {
682682
s_logger.debug("Service Offering host tag does not match the last host of this VM");
683683
return false;
684684
}

server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -379,17 +379,8 @@ private List<String> getHostManagedVms(Host host) {
379379
}
380380

381381
private boolean hostSupportsServiceOffering(HostVO host, ServiceOffering serviceOffering) {
382-
if (host == null) {
383-
return false;
384-
}
385-
if (serviceOffering == null) {
386-
return false;
387-
}
388-
if (Strings.isNullOrEmpty(serviceOffering.getHostTag())) {
389-
return true;
390-
}
391382
hostDao.loadHostTags(host);
392-
return host.getHostTags() != null && host.getHostTags().contains(serviceOffering.getHostTag());
383+
return host.checkHostServiceOfferingTags(serviceOffering);
393384
}
394385

395386
private boolean storagePoolSupportsDiskOffering(StoragePool pool, DiskOffering diskOffering) {

server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import com.cloud.host.Host;
8383
import com.cloud.host.HostVO;
8484
import com.cloud.host.Status;
85+
import com.cloud.host.dao.HostDao;
8586
import com.cloud.hypervisor.Hypervisor;
8687
import com.cloud.network.Network;
8788
import com.cloud.network.NetworkModel;
@@ -180,6 +181,8 @@ public class UnmanagedVMsManagerImplTest {
180181
private UserVmDao userVmDao;
181182
@Mock
182183
private NicDao nicDao;
184+
@Mock
185+
private HostDao hostDao;
183186

184187
@Mock
185188
private VMInstanceVO virtualMachine;
@@ -235,6 +238,7 @@ public void setUp() throws Exception {
235238
HostVO hostVO = Mockito.mock(HostVO.class);
236239
when(hostVO.isInMaintenanceStates()).thenReturn(false);
237240
hosts.add(hostVO);
241+
when(hostVO.checkHostServiceOfferingTags(Mockito.any())).thenReturn(true);
238242
when(resourceManager.listHostsInClusterByStatus(Mockito.anyLong(), Mockito.any(Status.class))).thenReturn(hosts);
239243
List<VMTemplateStoragePoolVO> templates = new ArrayList<>();
240244
when(templatePoolDao.listAll()).thenReturn(templates);
@@ -368,6 +372,7 @@ public void importUnmanagedInstanceTest() {
368372
when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance");
369373
when(importUnmanageInstanceCmd.getAccountName()).thenReturn(null);
370374
when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null);
375+
doNothing().when(hostDao).loadHostTags(null);
371376
PowerMockito.mockStatic(UsageEventUtils.class);
372377
unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd);
373378
}

0 commit comments

Comments
 (0)