You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by jo...@apache.org on 2024/09/10 11:12:47 UTC
(cloudstack) branch main updated: Enhance the `listAffinityGroups` API by adding the dedicated resources related to an affinity group (#9188)
This is an automated email from the ASF dual-hosted git repository.
joao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 6ec3c486ddb Enhance the `listAffinityGroups` API by adding the dedicated resources related to an affinity group (#9188)
6ec3c486ddb is described below
commit 6ec3c486ddb4d5daeba59e68e2634a446564f6dc
Author: Bernardo De Marco Gonçalves <be...@gmail.com>
AuthorDate: Tue Sep 10 08:12:41 2024 -0300
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
---
.../main/java/com/cloud/dc/DedicatedResources.java | 4 ++
.../cloudstack/affinity/AffinityGroupResponse.java | 13 +++++
.../dedicated/DedicatedResourceResponse.java | 44 ++++++++++++++++
.../dedicated/DedicatedResourceManagerImpl.java | 36 +++++++------
.../api/query/dao/AffinityGroupJoinDaoImpl.java | 61 +++++++++++++++++++++-
ui/public/locales/en.json | 1 +
ui/public/locales/pt_BR.json | 1 +
ui/src/components/view/DetailsTab.vue | 20 +++++++
ui/src/config/section/compute.js | 2 +-
9 files changed, 163 insertions(+), 19 deletions(-)
diff --git a/api/src/main/java/com/cloud/dc/DedicatedResources.java b/api/src/main/java/com/cloud/dc/DedicatedResources.java
index 63188ca0b0e..23e6cc88a1e 100644
--- a/api/src/main/java/com/cloud/dc/DedicatedResources.java
+++ b/api/src/main/java/com/cloud/dc/DedicatedResources.java
@@ -21,6 +21,10 @@ import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
public interface DedicatedResources extends InfrastructureEntity, InternalIdentity, Identity {
+ enum Type {
+ Zone, Pod, Cluster, Host
+ }
+
@Override
long getId();
diff --git a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java
index 6cda8d07bd8..69f391a5656 100644
--- a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java
@@ -25,6 +25,7 @@ import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
import org.apache.cloudstack.api.response.ControlledViewEntityResponse;
+import org.apache.cloudstack.dedicated.DedicatedResourceResponse;
import com.cloud.serializer.Param;
@@ -76,6 +77,10 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie
@Param(description = "virtual machine IDs associated with this affinity group")
private List<String> vmIdList;
+ @SerializedName("dedicatedresources")
+ @Param(description = "dedicated resources associated with this affinity group")
+ private List<DedicatedResourceResponse> dedicatedResources;
+
public AffinityGroupResponse() {
}
@@ -171,4 +176,12 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie
this.vmIdList.add(vmId);
}
+ public void addDedicatedResource(DedicatedResourceResponse dedicatedResourceResponse) {
+ if (this.dedicatedResources == null) {
+ this.dedicatedResources = new ArrayList<>();
+ }
+
+ this.dedicatedResources.add(dedicatedResourceResponse);
+ }
+
}
diff --git a/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java b/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java
new file mode 100644
index 00000000000..fc116cbb91e
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java
@@ -0,0 +1,44 @@
+// 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 org.apache.cloudstack.dedicated;
+
+import com.cloud.dc.DedicatedResources;
+import com.cloud.serializer.Param;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.cloudstack.api.BaseResponse;
+
+public class DedicatedResourceResponse extends BaseResponse {
+ @SerializedName("resourceid")
+ @Param(description = "the ID of the resource")
+ private String resourceId;
+
+ @SerializedName("resourcename")
+ @Param(description = "the name of the resource")
+ private String resourceName;
+
+ @SerializedName("resourcetype")
+ @Param(description = "the type of the resource")
+ private DedicatedResources.Type resourceType;
+
+ public DedicatedResourceResponse(String resourceId, String resourceName, DedicatedResources.Type resourceType) {
+ this.resourceId = resourceId;
+ this.resourceName = resourceName;
+ this.resourceType = resourceType;
+ }
+}
diff --git a/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java b/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java
index 9060eccb64a..4f1db396b7c 100644
--- a/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java
+++ b/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java
@@ -23,6 +23,7 @@ import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+import org.apache.commons.lang3.StringUtils;
import org.apache.cloudstack.affinity.AffinityGroup;
import org.apache.cloudstack.affinity.AffinityGroupService;
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
@@ -236,7 +237,7 @@ public class DedicatedResourceManagerImpl implements DedicatedService {
@Override
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
// find or create the affinity group by name under this account/domain
- AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
+ AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Zone);
if (group == null) {
logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
@@ -372,10 +373,10 @@ public class DedicatedResourceManagerImpl implements DedicatedService {
@Override
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
// find or create the affinity group by name under this account/domain
- AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
+ AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Pod);
if (group == null) {
- logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
- throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
+ logger.error("Unable to dedicate pod due to, failed to create dedication affinity group");
+ throw new CloudRuntimeException("Failed to dedicate pod. Please contact Cloud Support.");
}
DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, podId, null, null, null, null, group.getId());
try {
@@ -485,10 +486,10 @@ public class DedicatedResourceManagerImpl implements DedicatedService {
@Override
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
// find or create the affinity group by name under this account/domain
- AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
+ AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Cluster);
if (group == null) {
- logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
- throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
+ logger.error("Unable to dedicate cluster due to, failed to create dedication affinity group");
+ throw new CloudRuntimeException("Failed to dedicate cluster. Please contact Cloud Support.");
}
DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, clusterId, null, null, null, group.getId());
try {
@@ -582,10 +583,10 @@ public class DedicatedResourceManagerImpl implements DedicatedService {
@Override
public List<DedicatedResourceVO> doInTransaction(TransactionStatus status) {
// find or create the affinity group by name under this account/domain
- AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal);
+ AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Host);
if (group == null) {
- logger.error("Unable to dedicate zone due to, failed to create dedication affinity group");
- throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support.");
+ logger.error("Unable to dedicate host due to, failed to create dedication affinity group");
+ throw new CloudRuntimeException("Failed to dedicate host. Please contact Cloud Support.");
}
DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, null, hostId, null, null, group.getId());
try {
@@ -607,7 +608,7 @@ public class DedicatedResourceManagerImpl implements DedicatedService {
}
- private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long accountId) {
+ private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long accountId, DedicatedResources.Type dedicatedResource) {
if (domainId == null) {
return null;
}
@@ -624,24 +625,25 @@ public class DedicatedResourceManagerImpl implements DedicatedService {
if (group != null) {
return group;
}
- // default to a groupname with account/domain information
- affinityGroupName = "DedicatedGrp-" + accountName;
+ // defaults to a groupName with resourceType and account/domain information
+ affinityGroupName = String.format("Dedicated%sGrp-%s", dedicatedResource, accountName);
} else {
// domain level group
group = _affinityGroupDao.findDomainLevelGroupByType(domainId, "ExplicitDedication");
if (group != null) {
return group;
}
- // default to a groupname with account/domain information
+
+ // defaults to a groupName with resourceType and account/domain information
String domainName = _domainDao.findById(domainId).getName();
- affinityGroupName = "DedicatedGrp-domain-" + domainName;
+ affinityGroupName = String.format("Dedicated%sGrp-domain-%s", dedicatedResource, domainName);
}
- group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", "dedicated resources group");
+ String description = String.format("Dedicated %s group", StringUtils.lowerCase(dedicatedResource.toString()));
+ group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", description);
return group;
-
}
private List<UserVmVO> getVmsOnHost(long hostId) {
diff --git a/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java
index 2a876ea8226..a5fd2bf11f1 100644
--- a/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java
@@ -21,21 +21,46 @@ import java.util.List;
import javax.inject.Inject;
-
import org.apache.cloudstack.affinity.AffinityGroup;
import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.dedicated.DedicatedResourceResponse;
import com.cloud.api.ApiResponseHelper;
import com.cloud.api.query.vo.AffinityGroupJoinVO;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
+import com.cloud.dc.DataCenter;
+import com.cloud.dc.DedicatedResourceVO;
+import com.cloud.dc.DedicatedResources;
+import com.cloud.dc.HostPodVO;
+import com.cloud.dc.dao.ClusterDao;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.DedicatedResourceDao;
+import com.cloud.dc.dao.HostPodDao;
+import com.cloud.host.Host;
+import com.cloud.host.dao.HostDao;
+import com.cloud.org.Cluster;
+import com.cloud.user.AccountManager;
public class AffinityGroupJoinDaoImpl extends GenericDaoBase<AffinityGroupJoinVO, Long> implements AffinityGroupJoinDao {
@Inject
private ConfigurationDao _configDao;
+ @Inject
+ private DedicatedResourceDao dedicatedResourceDao;
+ @Inject
+ private DataCenterDao dataCenterDao;
+ @Inject
+ private HostPodDao podDao;
+ @Inject
+ private ClusterDao clusterDao;
+ @Inject
+ private HostDao hostDao;
+ @Inject
+ private AccountManager accountManager;
private final SearchBuilder<AffinityGroupJoinVO> agSearch;
@@ -64,6 +89,14 @@ public class AffinityGroupJoinDaoImpl extends GenericDaoBase<AffinityGroupJoinVO
ApiResponseHelper.populateOwner(agResponse, vag);
+ Long callerId = CallContext.current().getCallingAccountId();
+ boolean isCallerRootAdmin = accountManager.isRootAdmin(callerId);
+ boolean containsDedicatedResources = vag.getType().equals("ExplicitDedication");
+ if (isCallerRootAdmin && containsDedicatedResources) {
+ List<DedicatedResourceVO> dedicatedResources = dedicatedResourceDao.listByAffinityGroupId(vag.getId());
+ this.populateDedicatedResourcesField(dedicatedResources, agResponse);
+ }
+
// update vm information
long instanceId = vag.getVmId();
if (instanceId > 0) {
@@ -76,6 +109,32 @@ public class AffinityGroupJoinDaoImpl extends GenericDaoBase<AffinityGroupJoinVO
return agResponse;
}
+ private void populateDedicatedResourcesField(List<DedicatedResourceVO> dedicatedResources, AffinityGroupResponse agResponse) {
+ if (dedicatedResources.isEmpty()) {
+ return;
+ }
+
+ for (DedicatedResourceVO resource : dedicatedResources) {
+ DedicatedResourceResponse dedicatedResourceResponse = null;
+
+ if (resource.getDataCenterId() != null) {
+ DataCenter dataCenter = dataCenterDao.findById(resource.getDataCenterId());
+ dedicatedResourceResponse = new DedicatedResourceResponse(dataCenter.getUuid(), dataCenter.getName(), DedicatedResources.Type.Zone);
+ } else if (resource.getPodId() != null) {
+ HostPodVO pod = podDao.findById(resource.getPodId());
+ dedicatedResourceResponse = new DedicatedResourceResponse(pod.getUuid(), pod.getName(), DedicatedResources.Type.Pod);
+ } else if (resource.getClusterId() != null) {
+ Cluster cluster = clusterDao.findById(resource.getClusterId());
+ dedicatedResourceResponse = new DedicatedResourceResponse(cluster.getUuid(), cluster.getName(), DedicatedResources.Type.Cluster);
+ } else if (resource.getHostId() != null) {
+ Host host = hostDao.findById(resource.getHostId());
+ dedicatedResourceResponse = new DedicatedResourceResponse(host.getUuid(), host.getName(), DedicatedResources.Type.Host);
+ }
+
+ agResponse.addDedicatedResource(dedicatedResourceResponse);
+ }
+ }
+
@Override
public AffinityGroupResponse setAffinityGroupResponse(AffinityGroupResponse vagData, AffinityGroupJoinVO vag) {
// update vm information
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 6ca710d4095..7336c038c89 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -673,6 +673,7 @@
"label.dedicate.zone": "Dedicate zone",
"label.dedicated": "Dedicated",
"label.dedicated.vlan.vni.ranges": "Dedicated VLAN/VNI ranges",
+"label.dedicatedresources": "Dedicated resources",
"label.default": "Default",
"label.default.use": "Default use",
"label.default.view": "Default view",
diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json
index 79333c100d3..f02aee747eb 100644
--- a/ui/public/locales/pt_BR.json
+++ b/ui/public/locales/pt_BR.json
@@ -472,6 +472,7 @@
"label.dedicate.zone": "Zona dedicada",
"label.dedicated": "Dedicado",
"label.dedicated.vlan.vni.ranges": "Intervalo(s) de VLAN/VNI dedicados",
+"label.dedicatedresources": "Recursos dedicados",
"label.default": "Padr\u00e3o",
"label.default.use": "Uso padr\u00e3o",
"label.default.view": "Visualiza\u00e7\u00e3o padr\u00e3o",
diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue
index 3f6f37aa08b..f5f180c8f19 100644
--- a/ui/src/components/view/DetailsTab.vue
+++ b/ui/src/components/view/DetailsTab.vue
@@ -115,6 +115,15 @@
<div v-else-if="item === 'payload'" style="white-space: pre-wrap;">
{{ JSON.stringify(JSON.parse(dataResource[item]), null, 4) || dataResource[item] }}
</div>
+ <div v-else-if="item === 'dedicatedresources'">
+ <div v-for="(resource, idx) in sortDedicatedResourcesByName(dataResource[item])" :key="idx">
+ <div>
+ <router-link :to="getResourceLink(resource.resourcetype, resource.resourceid)">
+ {{ resource.resourcename }}
+ </router-link>
+ </div>
+ </div>
+ </div>
<div v-else>{{ dataResource[item] }}</div>
</div>
</a-list-item>
@@ -150,6 +159,7 @@
import DedicateData from './DedicateData'
import HostInfo from '@/views/infra/HostInfo'
import VmwareData from './VmwareData'
+import { genericCompare } from '@/utils/sort'
export default {
name: 'DetailsTab',
@@ -386,6 +396,16 @@ export default {
},
getDetailTitle (detail) {
return `label.${String(this.detailsTitles[detail]).toLowerCase()}`
+ },
+ getResourceLink (type, id) {
+ return `/${type.toLowerCase()}/${id}`
+ },
+ sortDedicatedResourcesByName (resources) {
+ resources.sort((resource, otherResource) => {
+ return genericCompare(resource.resourcename, otherResource.resourcename)
+ })
+
+ return resources
}
}
}
diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js
index b992229962b..c6d16d6ea78 100644
--- a/ui/src/config/section/compute.js
+++ b/ui/src/config/section/compute.js
@@ -988,7 +988,7 @@ export default {
}
return fields
},
- details: ['name', 'id', 'description', 'type', 'account', 'domain'],
+ details: ['name', 'id', 'description', 'type', 'account', 'domain', 'dedicatedresources'],
related: [{
name: 'vm',
title: 'label.instances',