Skip to content

Commit 6ec3c48

Browse files
Enhance the listAffinityGroups API by adding the dedicated resources related to an affinity group (#9188)
* add dedicated resource response * populate dedicatedresources field * change affinity group name and description when it contains dedicated resources * display dedicatedresources on UI * add end of line to DedicatedResourceResponse class * remove unnecessary fully qualified names
1 parent b1f683d commit 6ec3c48

File tree

9 files changed

+163
-19
lines changed

9 files changed

+163
-19
lines changed

api/src/main/java/com/cloud/dc/DedicatedResources.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
import org.apache.cloudstack.api.InternalIdentity;
2222

2323
public interface DedicatedResources extends InfrastructureEntity, InternalIdentity, Identity {
24+
enum Type {
25+
Zone, Pod, Cluster, Host
26+
}
27+
2428
@Override
2529
long getId();
2630

api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.cloudstack.api.BaseResponse;
2626
import org.apache.cloudstack.api.EntityReference;
2727
import org.apache.cloudstack.api.response.ControlledViewEntityResponse;
28+
import org.apache.cloudstack.dedicated.DedicatedResourceResponse;
2829

2930
import com.cloud.serializer.Param;
3031

@@ -76,6 +77,10 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie
7677
@Param(description = "virtual machine IDs associated with this affinity group")
7778
private List<String> vmIdList;
7879

80+
@SerializedName("dedicatedresources")
81+
@Param(description = "dedicated resources associated with this affinity group")
82+
private List<DedicatedResourceResponse> dedicatedResources;
83+
7984
public AffinityGroupResponse() {
8085
}
8186

@@ -171,4 +176,12 @@ public void addVMId(String vmId) {
171176
this.vmIdList.add(vmId);
172177
}
173178

179+
public void addDedicatedResource(DedicatedResourceResponse dedicatedResourceResponse) {
180+
if (this.dedicatedResources == null) {
181+
this.dedicatedResources = new ArrayList<>();
182+
}
183+
184+
this.dedicatedResources.add(dedicatedResourceResponse);
185+
}
186+
174187
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 org.apache.cloudstack.dedicated;
18+
19+
import com.cloud.dc.DedicatedResources;
20+
import com.cloud.serializer.Param;
21+
22+
import com.google.gson.annotations.SerializedName;
23+
24+
import org.apache.cloudstack.api.BaseResponse;
25+
26+
public class DedicatedResourceResponse extends BaseResponse {
27+
@SerializedName("resourceid")
28+
@Param(description = "the ID of the resource")
29+
private String resourceId;
30+
31+
@SerializedName("resourcename")
32+
@Param(description = "the name of the resource")
33+
private String resourceName;
34+
35+
@SerializedName("resourcetype")
36+
@Param(description = "the type of the resource")
37+
private DedicatedResources.Type resourceType;
38+
39+
public DedicatedResourceResponse(String resourceId, String resourceName, DedicatedResources.Type resourceType) {
40+
this.resourceId = resourceId;
41+
this.resourceName = resourceName;
42+
this.resourceType = resourceType;
43+
}
44+
}

plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import javax.inject.Inject;
2424
import javax.naming.ConfigurationException;
2525

26+
import org.apache.commons.lang3.StringUtils;
2627
import org.apache.cloudstack.affinity.AffinityGroup;
2728
import org.apache.cloudstack.affinity.AffinityGroupService;
2829
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
@@ -236,7 +237,7 @@ public List<DedicatedResourceVO> dedicateZone(final Long zoneId, final Long doma
236237
@Override
237238
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
238239
// find or create the affinity group by name under this account/domain
239-
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
240+
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Zone);
240241
if (group == null) {
241242
logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
242243
throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
@@ -372,10 +373,10 @@ public List<DedicatedResourceVO> dedicatePod(final Long podId, final Long domain
372373
@Override
373374
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
374375
// find or create the affinity group by name under this account/domain
375-
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
376+
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Pod);
376377
if (group == null) {
377-
logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
378-
throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
378+
logger.error("Unable to dedicate pod due to, failed to create dedication affinity group");
379+
throw new CloudRuntimeException("Failed to dedicate pod. Please contact Cloud Support.");
379380
}
380381
DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, podId, null, null, null, null, group.getId());
381382
try {
@@ -485,10 +486,10 @@ public List<DedicatedResourceVO> dedicateCluster(final Long clusterId, final Lon
485486
@Override
486487
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
487488
// find or create the affinity group by name under this account/domain
488-
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
489+
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Cluster);
489490
if (group == null) {
490-
logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
491-
throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
491+
logger.error("Unable to dedicate cluster due to, failed to create dedication affinity group");
492+
throw new CloudRuntimeException("Failed to dedicate cluster. Please contact Cloud Support.");
492493
}
493494
DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, clusterId, null, null, null, group.getId());
494495
try {
@@ -582,10 +583,10 @@ public List<DedicatedResourceVO> dedicateHost(final Long hostId, final Long doma
582583
@Override
583584
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
584585
// find or create the affinity group by name under this account/domain
585-
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
586+
AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Host);
586587
if (group == null) {
587-
logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
588-
throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
588+
logger.error("Unable to dedicate host due to, failed to create dedication affinity group");
589+
throw new CloudRuntimeException("Failed to dedicate host. Please contact Cloud Support.");
589590
}
590591
DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, null, hostId, null, null, group.getId());
591592
try {
@@ -607,7 +608,7 @@ public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
607608

608609
}
609610

610-
private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long accountId) {
611+
private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long accountId, DedicatedResources.Type dedicatedResource) {
611612
if (domainId == null) {
612613
return null;
613614
}
@@ -624,24 +625,25 @@ private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long acc
624625
if (group != null) {
625626
return group;
626627
}
627-
// default to a groupname with account/domain information
628-
affinityGroupName = "DedicatedGrp-" + accountName;
629628

629+
// defaults to a groupName with resourceType and account/domain information
630+
affinityGroupName = String.format("Dedicated%sGrp-%s", dedicatedResource, accountName);
630631
} else {
631632
// domain level group
632633
group = _affinityGroupDao.findDomainLevelGroupByType(domainId, "ExplicitDedication");
633634
if (group != null) {
634635
return group;
635636
}
636-
// default to a groupname with account/domain information
637+
638+
// defaults to a groupName with resourceType and account/domain information
637639
String domainName = _domainDao.findById(domainId).getName();
638-
affinityGroupName = "DedicatedGrp-domain-" + domainName;
640+
affinityGroupName = String.format("Dedicated%sGrp-domain-%s", dedicatedResource, domainName);
639641
}
640642

641-
group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", "dedicated resources group");
643+
String description = String.format("Dedicated %s group", StringUtils.lowerCase(dedicatedResource.toString()));
644+
group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", description);
642645

643646
return group;
644-
645647
}
646648

647649
private List<UserVmVO> getVmsOnHost(long hostId) {

server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,46 @@
2121

2222
import javax.inject.Inject;
2323

24-
2524
import org.apache.cloudstack.affinity.AffinityGroup;
2625
import org.apache.cloudstack.affinity.AffinityGroupResponse;
2726
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
27+
import org.apache.cloudstack.context.CallContext;
28+
import org.apache.cloudstack.dedicated.DedicatedResourceResponse;
2829

2930
import com.cloud.api.ApiResponseHelper;
3031
import com.cloud.api.query.vo.AffinityGroupJoinVO;
3132
import com.cloud.utils.db.GenericDaoBase;
3233
import com.cloud.utils.db.SearchBuilder;
3334
import com.cloud.utils.db.SearchCriteria;
35+
import com.cloud.dc.DataCenter;
36+
import com.cloud.dc.DedicatedResourceVO;
37+
import com.cloud.dc.DedicatedResources;
38+
import com.cloud.dc.HostPodVO;
39+
import com.cloud.dc.dao.ClusterDao;
40+
import com.cloud.dc.dao.DataCenterDao;
41+
import com.cloud.dc.dao.DedicatedResourceDao;
42+
import com.cloud.dc.dao.HostPodDao;
43+
import com.cloud.host.Host;
44+
import com.cloud.host.dao.HostDao;
45+
import com.cloud.org.Cluster;
46+
import com.cloud.user.AccountManager;
3447

3548
public class AffinityGroupJoinDaoImpl extends GenericDaoBase<AffinityGroupJoinVO, Long> implements AffinityGroupJoinDao {
3649

3750
@Inject
3851
private ConfigurationDao _configDao;
52+
@Inject
53+
private DedicatedResourceDao dedicatedResourceDao;
54+
@Inject
55+
private DataCenterDao dataCenterDao;
56+
@Inject
57+
private HostPodDao podDao;
58+
@Inject
59+
private ClusterDao clusterDao;
60+
@Inject
61+
private HostDao hostDao;
62+
@Inject
63+
private AccountManager accountManager;
3964

4065
private final SearchBuilder<AffinityGroupJoinVO> agSearch;
4166

@@ -64,6 +89,14 @@ public AffinityGroupResponse newAffinityGroupResponse(AffinityGroupJoinVO vag) {
6489

6590
ApiResponseHelper.populateOwner(agResponse, vag);
6691

92+
Long callerId = CallContext.current().getCallingAccountId();
93+
boolean isCallerRootAdmin = accountManager.isRootAdmin(callerId);
94+
boolean containsDedicatedResources = vag.getType().equals("ExplicitDedication");
95+
if (isCallerRootAdmin && containsDedicatedResources) {
96+
List<DedicatedResourceVO> dedicatedResources = dedicatedResourceDao.listByAffinityGroupId(vag.getId());
97+
this.populateDedicatedResourcesField(dedicatedResources, agResponse);
98+
}
99+
67100
// update vm information
68101
long instanceId = vag.getVmId();
69102
if (instanceId > 0) {
@@ -76,6 +109,32 @@ public AffinityGroupResponse newAffinityGroupResponse(AffinityGroupJoinVO vag) {
76109
return agResponse;
77110
}
78111

112+
private void populateDedicatedResourcesField(List<DedicatedResourceVO> dedicatedResources, AffinityGroupResponse agResponse) {
113+
if (dedicatedResources.isEmpty()) {
114+
return;
115+
}
116+
117+
for (DedicatedResourceVO resource : dedicatedResources) {
118+
DedicatedResourceResponse dedicatedResourceResponse = null;
119+
120+
if (resource.getDataCenterId() != null) {
121+
DataCenter dataCenter = dataCenterDao.findById(resource.getDataCenterId());
122+
dedicatedResourceResponse = new DedicatedResourceResponse(dataCenter.getUuid(), dataCenter.getName(), DedicatedResources.Type.Zone);
123+
} else if (resource.getPodId() != null) {
124+
HostPodVO pod = podDao.findById(resource.getPodId());
125+
dedicatedResourceResponse = new DedicatedResourceResponse(pod.getUuid(), pod.getName(), DedicatedResources.Type.Pod);
126+
} else if (resource.getClusterId() != null) {
127+
Cluster cluster = clusterDao.findById(resource.getClusterId());
128+
dedicatedResourceResponse = new DedicatedResourceResponse(cluster.getUuid(), cluster.getName(), DedicatedResources.Type.Cluster);
129+
} else if (resource.getHostId() != null) {
130+
Host host = hostDao.findById(resource.getHostId());
131+
dedicatedResourceResponse = new DedicatedResourceResponse(host.getUuid(), host.getName(), DedicatedResources.Type.Host);
132+
}
133+
134+
agResponse.addDedicatedResource(dedicatedResourceResponse);
135+
}
136+
}
137+
79138
@Override
80139
public AffinityGroupResponse setAffinityGroupResponse(AffinityGroupResponse vagData, AffinityGroupJoinVO vag) {
81140
// update vm information

ui/public/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@
673673
"label.dedicate.zone": "Dedicate zone",
674674
"label.dedicated": "Dedicated",
675675
"label.dedicated.vlan.vni.ranges": "Dedicated VLAN/VNI ranges",
676+
"label.dedicatedresources": "Dedicated resources",
676677
"label.default": "Default",
677678
"label.default.use": "Default use",
678679
"label.default.view": "Default view",

ui/public/locales/pt_BR.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@
472472
"label.dedicate.zone": "Zona dedicada",
473473
"label.dedicated": "Dedicado",
474474
"label.dedicated.vlan.vni.ranges": "Intervalo(s) de VLAN/VNI dedicados",
475+
"label.dedicatedresources": "Recursos dedicados",
475476
"label.default": "Padr\u00e3o",
476477
"label.default.use": "Uso padr\u00e3o",
477478
"label.default.view": "Visualiza\u00e7\u00e3o padr\u00e3o",

ui/src/components/view/DetailsTab.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@
115115
<div v-else-if="item === 'payload'" style="white-space: pre-wrap;">
116116
{{ JSON.stringify(JSON.parse(dataResource[item]), null, 4) || dataResource[item] }}
117117
</div>
118+
<div v-else-if="item === 'dedicatedresources'">
119+
<div v-for="(resource, idx) in sortDedicatedResourcesByName(dataResource[item])" :key="idx">
120+
<div>
121+
<router-link :to="getResourceLink(resource.resourcetype, resource.resourceid)">
122+
{{ resource.resourcename }}
123+
</router-link>
124+
</div>
125+
</div>
126+
</div>
118127
<div v-else>{{ dataResource[item] }}</div>
119128
</div>
120129
</a-list-item>
@@ -150,6 +159,7 @@
150159
import DedicateData from './DedicateData'
151160
import HostInfo from '@/views/infra/HostInfo'
152161
import VmwareData from './VmwareData'
162+
import { genericCompare } from '@/utils/sort'
153163
154164
export default {
155165
name: 'DetailsTab',
@@ -386,6 +396,16 @@ export default {
386396
},
387397
getDetailTitle (detail) {
388398
return `label.${String(this.detailsTitles[detail]).toLowerCase()}`
399+
},
400+
getResourceLink (type, id) {
401+
return `/${type.toLowerCase()}/${id}`
402+
},
403+
sortDedicatedResourcesByName (resources) {
404+
resources.sort((resource, otherResource) => {
405+
return genericCompare(resource.resourcename, otherResource.resourcename)
406+
})
407+
408+
return resources
389409
}
390410
}
391411
}

ui/src/config/section/compute.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ export default {
988988
}
989989
return fields
990990
},
991-
details: ['name', 'id', 'description', 'type', 'account', 'domain'],
991+
details: ['name', 'id', 'description', 'type', 'account', 'domain', 'dedicatedresources'],
992992
related: [{
993993
name: 'vm',
994994
title: 'label.instances',

0 commit comments

Comments
 (0)