mirror of https://github.com/apache/cloudstack.git
extensions: allow reserved resource details
Adds a new request parameter for create/updateExtension API to allow operator to provide detail names for the extension resources which will be reserved to be used by the extension. The end user won't be able to view or add details with these details names for the resource. Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
d11d182c71
commit
95816b44e9
|
|
@ -502,6 +502,7 @@ public class ApiConstants {
|
|||
public static final String RECOVER = "recover";
|
||||
public static final String REPAIR = "repair";
|
||||
public static final String REQUIRES_HVM = "requireshvm";
|
||||
public static final String RESERVED_RESOURCE_DETAILS = "reservedresourcedetails";
|
||||
public static final String RESOURCES = "resources";
|
||||
public static final String RESOURCE_COUNT = "resourcecount";
|
||||
public static final String RESOURCE_NAME = "resourcename";
|
||||
|
|
|
|||
|
|
@ -85,6 +85,12 @@ public class ExtensionResponse extends BaseResponse {
|
|||
@Param(description = "Removal timestamp of the extension, if applicable")
|
||||
private Date removed;
|
||||
|
||||
@SerializedName(ApiConstants.RESERVED_RESOURCE_DETAILS)
|
||||
@Param(description = "Resource detail names as comma separated string that should be reserved and not visible " +
|
||||
"to end users",
|
||||
since = "4.22.1")
|
||||
protected String reservedResourceDetails;
|
||||
|
||||
public ExtensionResponse(String id, String name, String description, String type) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
|
|
@ -179,4 +185,8 @@ public class ExtensionResponse extends BaseResponse {
|
|||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public void setReservedResourceDetails(String reservedResourceDetails) {
|
||||
this.reservedResourceDetails = reservedResourceDetails;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
|
||||
package org.apache.cloudstack.extension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ExtensionHelper {
|
||||
Long getExtensionIdForCluster(long clusterId);
|
||||
Extension getExtension(long id);
|
||||
Extension getExtensionForCluster(long clusterId);
|
||||
List<String> getExtensionReservedResourceDetails(long extensionId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ SELECT
|
|||
`vm_template`.`format` AS `template_format`,
|
||||
`vm_template`.`display_text` AS `template_display_text`,
|
||||
`vm_template`.`enable_password` AS `password_enabled`,
|
||||
`vm_template`.`extension_id` AS `template_extension_id`,
|
||||
`iso`.`id` AS `iso_id`,
|
||||
`iso`.`uuid` AS `iso_uuid`,
|
||||
`iso`.`name` AS `iso_name`,
|
||||
|
|
|
|||
|
|
@ -83,6 +83,12 @@ public class CreateExtensionCmd extends BaseCmd {
|
|||
description = "Details in key/value pairs using format details[i].keyname=keyvalue. Example: details[0].endpoint.url=urlvalue")
|
||||
protected Map details;
|
||||
|
||||
@Parameter(name = ApiConstants.RESERVED_RESOURCE_DETAILS, type = CommandType.STRING,
|
||||
description = "Resource detail names as comma separated string that should be reserved and not visible " +
|
||||
"to end users",
|
||||
since = "4.22.1")
|
||||
protected String reservedResourceDetails;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -115,6 +121,10 @@ public class CreateExtensionCmd extends BaseCmd {
|
|||
return convertDetailsToMap(details);
|
||||
}
|
||||
|
||||
public String getReservedResourceDetails() {
|
||||
return reservedResourceDetails;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -78,6 +78,12 @@ public class UpdateExtensionCmd extends BaseCmd {
|
|||
"if false or not set, no action)")
|
||||
private Boolean cleanupDetails;
|
||||
|
||||
@Parameter(name = ApiConstants.RESERVED_RESOURCE_DETAILS, type = CommandType.STRING,
|
||||
description = "Resource detail names as comma separated string that should be reserved and not visible " +
|
||||
"to end users",
|
||||
since = "4.22.1")
|
||||
protected String reservedResourceDetails;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -106,6 +112,10 @@ public class UpdateExtensionCmd extends BaseCmd {
|
|||
return cleanupDetails;
|
||||
}
|
||||
|
||||
public String getReservedResourceDetails() {
|
||||
return reservedResourceDetails;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -216,6 +216,11 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
@Inject
|
||||
AccountService accountService;
|
||||
|
||||
// Map of in-built extension names and their reserved resource details that shouldn't be accessible to end-users
|
||||
protected static final Map<String, List<String>> INBUILT_RESERVED_RESOURCE_DETAILS = Map.of(
|
||||
"proxmox", List.of("proxmox_vmid")
|
||||
);
|
||||
|
||||
private ScheduledExecutorService extensionPathStateCheckExecutor;
|
||||
|
||||
protected String getDefaultExtensionRelativePath(String name) {
|
||||
|
|
@ -563,6 +568,25 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
updateExtensionPathReady(extension, true);
|
||||
}
|
||||
|
||||
protected void addInbuiltExtensionReservedResourceDetails(long extensionId, List<String> reservedResourceDetails) {
|
||||
ExtensionVO vo = extensionDao.findById(extensionId);
|
||||
if (vo == null || vo.isUserDefined()) {
|
||||
return;
|
||||
}
|
||||
String lowerName = StringUtils.defaultString(vo.getName()).toLowerCase();
|
||||
Optional<Map.Entry<String, List<String>>> match = INBUILT_RESERVED_RESOURCE_DETAILS.entrySet().stream()
|
||||
.filter(e -> lowerName.contains(e.getKey().toLowerCase()))
|
||||
.findFirst();
|
||||
if (match.isPresent()) {
|
||||
Set<String> existing = new HashSet<>(reservedResourceDetails);
|
||||
for (String detailKey : match.get().getValue()) {
|
||||
if (existing.add(detailKey)) {
|
||||
reservedResourceDetails.add(detailKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtensionsPath() {
|
||||
return externalProvisioner.getExtensionsPath();
|
||||
|
|
@ -577,6 +601,7 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
String relativePath = cmd.getPath();
|
||||
final Boolean orchestratorRequiresPrepareVm = cmd.isOrchestratorRequiresPrepareVm();
|
||||
final String stateStr = cmd.getState();
|
||||
final String reservedResourceDetails = cmd.getReservedResourceDetails();
|
||||
ExtensionVO extensionByName = extensionDao.findByName(name);
|
||||
if (extensionByName != null) {
|
||||
throw new CloudRuntimeException("Extension by name already exists");
|
||||
|
|
@ -624,6 +649,10 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM, String.valueOf(orchestratorRequiresPrepareVm),
|
||||
false));
|
||||
}
|
||||
if (StringUtils.isNotBlank(reservedResourceDetails)) {
|
||||
detailsVOList.add(new ExtensionDetailsVO(extension.getId(),
|
||||
ApiConstants.RESERVED_RESOURCE_DETAILS, reservedResourceDetails, false));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(detailsVOList)) {
|
||||
extensionDetailsDao.saveDetails(detailsVOList);
|
||||
}
|
||||
|
|
@ -704,6 +733,7 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
final String stateStr = cmd.getState();
|
||||
final Map<String, String> details = cmd.getDetails();
|
||||
final Boolean cleanupDetails = cmd.isCleanupDetails();
|
||||
final String reservedResourceDetails = cmd.getReservedResourceDetails();
|
||||
final ExtensionVO extensionVO = extensionDao.findById(id);
|
||||
if (extensionVO == null) {
|
||||
throw new InvalidParameterValueException("Failed to find the extension");
|
||||
|
|
@ -732,7 +762,8 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
throw new CloudRuntimeException(String.format("Failed to updated the extension: %s",
|
||||
extensionVO.getName()));
|
||||
}
|
||||
updateExtensionsDetails(cleanupDetails, details, orchestratorRequiresPrepareVm, id);
|
||||
updateExtensionsDetails(cleanupDetails, details, orchestratorRequiresPrepareVm, reservedResourceDetails,
|
||||
id);
|
||||
return extensionVO;
|
||||
});
|
||||
if (StringUtils.isNotBlank(stateStr)) {
|
||||
|
|
@ -748,9 +779,11 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
return result;
|
||||
}
|
||||
|
||||
protected void updateExtensionsDetails(Boolean cleanupDetails, Map<String, String> details, Boolean orchestratorRequiresPrepareVm, long id) {
|
||||
protected void updateExtensionsDetails(Boolean cleanupDetails, Map<String, String> details,
|
||||
Boolean orchestratorRequiresPrepareVm, String reservedResourceDetails, long id) {
|
||||
final boolean needToUpdateAllDetails = Boolean.TRUE.equals(cleanupDetails) || MapUtils.isNotEmpty(details);
|
||||
if (!needToUpdateAllDetails && orchestratorRequiresPrepareVm == null) {
|
||||
if (!needToUpdateAllDetails && orchestratorRequiresPrepareVm == null &&
|
||||
StringUtils.isBlank(reservedResourceDetails)) {
|
||||
return;
|
||||
}
|
||||
if (needToUpdateAllDetails) {
|
||||
|
|
@ -761,6 +794,9 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
hiddenDetails.put(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
|
||||
String.valueOf(orchestratorRequiresPrepareVm));
|
||||
}
|
||||
if (StringUtils.isNotBlank(reservedResourceDetails)) {
|
||||
hiddenDetails.put(ApiConstants.RESERVED_RESOURCE_DETAILS, reservedResourceDetails);
|
||||
}
|
||||
if (MapUtils.isNotEmpty(hiddenDetails)) {
|
||||
hiddenDetails.forEach((key, value) -> detailsVOList.add(
|
||||
new ExtensionDetailsVO(id, key, value, false)));
|
||||
|
|
@ -775,15 +811,29 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
extensionDetailsDao.removeDetails(id);
|
||||
}
|
||||
} else {
|
||||
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(id,
|
||||
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM);
|
||||
if (detailsVO == null) {
|
||||
extensionDetailsDao.persist(new ExtensionDetailsVO(id,
|
||||
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
|
||||
String.valueOf(orchestratorRequiresPrepareVm), false));
|
||||
} else if (Boolean.parseBoolean(detailsVO.getValue()) != orchestratorRequiresPrepareVm) {
|
||||
detailsVO.setValue(String.valueOf(orchestratorRequiresPrepareVm));
|
||||
extensionDetailsDao.update(detailsVO.getId(), detailsVO);
|
||||
if (orchestratorRequiresPrepareVm != null) {
|
||||
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(id,
|
||||
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM);
|
||||
if (detailsVO == null) {
|
||||
extensionDetailsDao.persist(new ExtensionDetailsVO(id,
|
||||
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
|
||||
String.valueOf(orchestratorRequiresPrepareVm), false));
|
||||
} else if (Boolean.parseBoolean(detailsVO.getValue()) != orchestratorRequiresPrepareVm) {
|
||||
detailsVO.setValue(String.valueOf(orchestratorRequiresPrepareVm));
|
||||
extensionDetailsDao.update(detailsVO.getId(), detailsVO);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotBlank(reservedResourceDetails)) {
|
||||
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(id,
|
||||
ApiConstants.RESERVED_RESOURCE_DETAILS);
|
||||
if (detailsVO == null) {
|
||||
extensionDetailsDao.persist(new ExtensionDetailsVO(id,
|
||||
ApiConstants.RESERVED_RESOURCE_DETAILS,
|
||||
reservedResourceDetails, false));
|
||||
} else if (!reservedResourceDetails.equals(detailsVO.getValue())) {
|
||||
detailsVO.setValue(reservedResourceDetails);
|
||||
extensionDetailsDao.update(detailsVO.getId(), detailsVO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -961,12 +1011,16 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
hiddenDetails = extensionDetails.second();
|
||||
} else {
|
||||
hiddenDetails = extensionDetailsDao.listDetailsKeyPairs(extension.getId(),
|
||||
List.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM));
|
||||
List.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
|
||||
ApiConstants.RESERVED_RESOURCE_DETAILS));
|
||||
}
|
||||
if (hiddenDetails.containsKey(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM)) {
|
||||
response.setOrchestratorRequiresPrepareVm(Boolean.parseBoolean(
|
||||
hiddenDetails.get(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM)));
|
||||
}
|
||||
if (hiddenDetails.containsKey(ApiConstants.RESERVED_RESOURCE_DETAILS)) {
|
||||
response.setReservedResourceDetails(hiddenDetails.get(ApiConstants.RESERVED_RESOURCE_DETAILS));
|
||||
}
|
||||
response.setObjectName(Extension.class.getSimpleName().toLowerCase());
|
||||
return response;
|
||||
}
|
||||
|
|
@ -1605,6 +1659,24 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
|
|||
return extensionDao.findById(extensionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExtensionReservedResourceDetails(long extensionId) {
|
||||
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(extensionId,
|
||||
ApiConstants.RESERVED_RESOURCE_DETAILS);
|
||||
if (detailsVO == null || !StringUtils.isNotBlank(detailsVO.getValue())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> reservedDetails = new ArrayList<>();
|
||||
String[] parts = detailsVO.getValue().split(",");
|
||||
for (String part : parts) {
|
||||
if (StringUtils.isNotBlank(part)) {
|
||||
reservedDetails.add(part.trim());
|
||||
}
|
||||
}
|
||||
addInbuiltExtensionReservedResourceDetails(extensionId, reservedDetails);
|
||||
return reservedDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
long pathStateCheckInterval = PathStateCheckInterval.value();
|
||||
|
|
|
|||
|
|
@ -94,4 +94,18 @@ public class CreateExtensionCmdTest {
|
|||
setField(cmd, "details", details);
|
||||
assertTrue(MapUtils.isNotEmpty(cmd.getDetails()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReservedResourceDetailsReturnsValueWhenSet() {
|
||||
setField(cmd, "reservedResourceDetails", "detail1,detail2,detail3");
|
||||
String result = cmd.getReservedResourceDetails();
|
||||
assertEquals("detail1,detail2,detail3", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReservedResourceDetailsReturnsNullWhenNotSet() {
|
||||
setField(cmd, "reservedResourceDetails", null);
|
||||
String result = cmd.getReservedResourceDetails();
|
||||
assertNull(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import static org.mockito.Mockito.doNothing;
|
|||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.util.ReflectionTestUtils.setField;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -134,6 +135,20 @@ public class UpdateExtensionCmdTest {
|
|||
assertTrue(cmd.isCleanupDetails());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReservedResourceDetailsReturnsValueWhenSet() {
|
||||
setField(cmd, "reservedResourceDetails", "detail1,detail2,detail3");
|
||||
String result = cmd.getReservedResourceDetails();
|
||||
assertEquals("detail1,detail2,detail3", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReservedResourceDetailsReturnsNullWhenNotSet() {
|
||||
setField(cmd, "reservedResourceDetails", null);
|
||||
String result = cmd.getReservedResourceDetails();
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeSetsExtensionResponseWhenManagerSucceeds() {
|
||||
Extension extension = mock(Extension.class);
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ import static org.junit.Assert.assertNotNull;
|
|||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyBoolean;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
|
|
@ -40,6 +42,7 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
import java.io.File;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
|
@ -49,8 +52,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.user.AccountService;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
|
|
@ -85,9 +86,11 @@ import org.apache.cloudstack.framework.extensions.dao.ExtensionResourceMapDao;
|
|||
import org.apache.cloudstack.framework.extensions.dao.ExtensionResourceMapDetailsDao;
|
||||
import org.apache.cloudstack.framework.extensions.vo.ExtensionCustomActionDetailsVO;
|
||||
import org.apache.cloudstack.framework.extensions.vo.ExtensionCustomActionVO;
|
||||
import org.apache.cloudstack.framework.extensions.vo.ExtensionDetailsVO;
|
||||
import org.apache.cloudstack.framework.extensions.vo.ExtensionResourceMapVO;
|
||||
import org.apache.cloudstack.framework.extensions.vo.ExtensionVO;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -113,6 +116,7 @@ import com.cloud.dc.dao.ClusterDao;
|
|||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.host.dao.HostDetailsDao;
|
||||
|
|
@ -122,6 +126,7 @@ import com.cloud.org.Cluster;
|
|||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.UuidUtils;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
|
|
@ -664,6 +669,8 @@ public class ExtensionsManagerImplTest {
|
|||
when(cmd.getPath()).thenReturn(null);
|
||||
when(cmd.isOrchestratorRequiresPrepareVm()).thenReturn(null);
|
||||
when(cmd.getState()).thenReturn(null);
|
||||
String reservedResourceDetails = "abc,xyz";
|
||||
when(cmd.getReservedResourceDetails()).thenReturn(reservedResourceDetails);
|
||||
when(extensionDao.findByName("ext1")).thenReturn(null);
|
||||
when(extensionDao.persist(any())).thenAnswer(inv -> {
|
||||
ExtensionVO extensionVO = inv.getArgument(0);
|
||||
|
|
@ -671,11 +678,20 @@ public class ExtensionsManagerImplTest {
|
|||
return extensionVO;
|
||||
});
|
||||
when(managementServerHostDao.listBy(any())).thenReturn(Collections.emptyList());
|
||||
|
||||
List<ExtensionDetailsVO> detailsList = new ArrayList<>();
|
||||
doAnswer(inv -> {
|
||||
List<ExtensionDetailsVO> detailsVO = inv.getArgument(0);
|
||||
detailsList.addAll(detailsVO);
|
||||
return null;
|
||||
}).when(extensionDetailsDao).saveDetails(anyList());
|
||||
Extension ext = extensionsManager.createExtension(cmd);
|
||||
|
||||
assertEquals("ext1", ext.getName());
|
||||
verify(extensionDao).persist(any());
|
||||
assertTrue(CollectionUtils.isNotEmpty(detailsList));
|
||||
assertTrue(detailsList.stream()
|
||||
.anyMatch(detail -> ApiConstants.RESERVED_RESOURCE_DETAILS.equals(detail.getName())
|
||||
&& reservedResourceDetails.equals(detail.getValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -938,14 +954,32 @@ public class ExtensionsManagerImplTest {
|
|||
public void updateExtensionsDetails_SavesDetails_WhenDetailsProvided() {
|
||||
long extensionId = 10L;
|
||||
Map<String, String> details = Map.of("foo", "bar", "baz", "qux");
|
||||
extensionsManager.updateExtensionsDetails(false, details, null, extensionId);
|
||||
extensionsManager.updateExtensionsDetails(false, details, null, null, extensionId);
|
||||
verify(extensionDetailsDao).saveDetails(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExtensionsDetails_PersistReservedDetail_WhenProvided() {
|
||||
long extensionId = 10L;
|
||||
when(extensionDetailsDao.persist(any())).thenReturn(mock(ExtensionDetailsVO.class));
|
||||
extensionsManager.updateExtensionsDetails(false, null, null, "abc,xyz", extensionId);
|
||||
verify(extensionDetailsDao).persist(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExtensionsDetails_UpdateReservedDetail_WhenProvided() {
|
||||
long extensionId = 10L;
|
||||
when(extensionDetailsDao.findDetail(anyLong(), eq(ApiConstants.RESERVED_RESOURCE_DETAILS)))
|
||||
.thenReturn(mock(ExtensionDetailsVO.class));
|
||||
when(extensionDetailsDao.update(anyLong(), any())).thenReturn(true);
|
||||
extensionsManager.updateExtensionsDetails(false, null, null, "abc,xyz", extensionId);
|
||||
verify(extensionDetailsDao).update(anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExtensionsDetails_DoesNothing_WhenDetailsAndCleanupAreNull() {
|
||||
long extensionId = 11L;
|
||||
extensionsManager.updateExtensionsDetails(null, null, null, extensionId);
|
||||
extensionsManager.updateExtensionsDetails(null, null, null, null, extensionId);
|
||||
verify(extensionDetailsDao, never()).removeDetails(anyLong());
|
||||
verify(extensionDetailsDao, never()).saveDetails(any());
|
||||
}
|
||||
|
|
@ -953,7 +987,7 @@ public class ExtensionsManagerImplTest {
|
|||
@Test
|
||||
public void updateExtensionsDetails_RemovesDetailsOnly_WhenCleanupIsTrue() {
|
||||
long extensionId = 12L;
|
||||
extensionsManager.updateExtensionsDetails(true, null, null, extensionId);
|
||||
extensionsManager.updateExtensionsDetails(true, null, null, null, extensionId);
|
||||
verify(extensionDetailsDao).removeDetails(extensionId);
|
||||
verify(extensionDetailsDao, never()).saveDetails(any());
|
||||
}
|
||||
|
|
@ -961,7 +995,7 @@ public class ExtensionsManagerImplTest {
|
|||
@Test
|
||||
public void updateExtensionsDetails_PersistsOrchestratorFlag_WhenFlagIsNotNull() {
|
||||
long extensionId = 13L;
|
||||
extensionsManager.updateExtensionsDetails(false, null, true, extensionId);
|
||||
extensionsManager.updateExtensionsDetails(false, null, true, null, extensionId);
|
||||
verify(extensionDetailsDao).persist(any());
|
||||
}
|
||||
|
||||
|
|
@ -970,7 +1004,7 @@ public class ExtensionsManagerImplTest {
|
|||
long extensionId = 14L;
|
||||
Map<String, String> details = Map.of("foo", "bar");
|
||||
doThrow(CloudRuntimeException.class).when(extensionDetailsDao).saveDetails(any());
|
||||
extensionsManager.updateExtensionsDetails(false, details, null, extensionId);
|
||||
extensionsManager.updateExtensionsDetails(false, details, null, null, extensionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1161,7 +1195,8 @@ public class ExtensionsManagerImplTest {
|
|||
when(externalProvisioner.getExtensionPath("entry2.sh")).thenReturn("/some/path/entry2.sh");
|
||||
|
||||
Map<String, String> hiddenDetails = Map.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM, "false");
|
||||
when(extensionDetailsDao.listDetailsKeyPairs(2L, List.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM)))
|
||||
when(extensionDetailsDao.listDetailsKeyPairs(2L, List.of(
|
||||
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM, ApiConstants.RESERVED_RESOURCE_DETAILS)))
|
||||
.thenReturn(hiddenDetails);
|
||||
|
||||
EnumSet<ApiConstants.ExtensionDetails> viewDetails = EnumSet.noneOf(ApiConstants.ExtensionDetails.class);
|
||||
|
|
@ -2069,4 +2104,118 @@ public class ExtensionsManagerImplTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExtensionReservedResourceDetailsReturnsEmptyListWhenDetailsNotFound() {
|
||||
long extensionId = 1L;
|
||||
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(null);
|
||||
|
||||
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExtensionReservedResourceDetailsReturnsEmptyListWhenValueIsBlank() {
|
||||
long extensionId = 2L;
|
||||
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
|
||||
when(detailsVO.getValue()).thenReturn(" ");
|
||||
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
|
||||
|
||||
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExtensionReservedResourceDetailsReturnsListOfTrimmedDetails() {
|
||||
long extensionId = 3L;
|
||||
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
|
||||
when(detailsVO.getValue()).thenReturn(" detail1 , detail2,detail3 ");
|
||||
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
|
||||
|
||||
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(3, result.size());
|
||||
assertEquals("detail1", result.get(0));
|
||||
assertEquals("detail2", result.get(1));
|
||||
assertEquals("detail3", result.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExtensionReservedResourceDetailsHandlesEmptyPartsGracefully() {
|
||||
long extensionId = 4L;
|
||||
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
|
||||
when(detailsVO.getValue()).thenReturn("detail1,,detail2, ,detail3");
|
||||
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
|
||||
|
||||
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(3, result.size());
|
||||
assertEquals("detail1", result.get(0));
|
||||
assertEquals("detail2", result.get(1));
|
||||
assertEquals("detail3", result.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getExtensionReservedResourceDetailsReturnsEmptyListWhenSplitResultsInNoParts() {
|
||||
long extensionId = 5L;
|
||||
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
|
||||
when(detailsVO.getValue()).thenReturn(",");
|
||||
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
|
||||
|
||||
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
|
||||
|
||||
assertNotNull(result);
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addInbuiltExtensionReservedResourceDetailsDoesNothingWhenExtensionNotFound() {
|
||||
when(extensionDao.findById(1L)).thenReturn(null);
|
||||
List<String> reservedResourceDetails = new ArrayList<>();
|
||||
extensionsManager.addInbuiltExtensionReservedResourceDetails(1L, reservedResourceDetails);
|
||||
assertTrue(reservedResourceDetails.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addInbuiltExtensionReservedResourceDetailsDoesNothingForUserDefinedExtension() {
|
||||
ExtensionVO extension = mock(ExtensionVO.class);
|
||||
when(extension.isUserDefined()).thenReturn(true);
|
||||
when(extensionDao.findById(2L)).thenReturn(extension);
|
||||
List<String> reservedResourceDetails = new ArrayList<>();
|
||||
reservedResourceDetails.add("existing-detail");
|
||||
extensionsManager.addInbuiltExtensionReservedResourceDetails(2L, reservedResourceDetails);
|
||||
assertEquals(1, reservedResourceDetails.size());
|
||||
assertTrue(reservedResourceDetails.contains("existing-detail"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addInbuiltExtensionReservedResourceDetailsDoesNothingWhenNoMatchFound() {
|
||||
ExtensionVO extension = mock(ExtensionVO.class);
|
||||
when(extension.isUserDefined()).thenReturn(false);
|
||||
when(extension.getName()).thenReturn("no-such-inbuilt-key-expected");
|
||||
when(extensionDao.findById(3L)).thenReturn(extension);
|
||||
List<String> reservedResourceDetails = new ArrayList<>();
|
||||
extensionsManager.addInbuiltExtensionReservedResourceDetails(3L, reservedResourceDetails);
|
||||
assertTrue(reservedResourceDetails.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addInbuiltExtensionReservedResourceDetailsAddedDetails() {
|
||||
ExtensionVO extension = mock(ExtensionVO.class);
|
||||
when(extension.isUserDefined()).thenReturn(false);
|
||||
Map.Entry<String, List<String>> entry =
|
||||
ExtensionsManagerImpl.INBUILT_RESERVED_RESOURCE_DETAILS.entrySet().iterator().next();
|
||||
when(extension.getName()).thenReturn(entry.getKey());
|
||||
when(extensionDao.findById(3L)).thenReturn(extension);
|
||||
List<String> reservedResourceDetails = new ArrayList<>();
|
||||
extensionsManager.addInbuiltExtensionReservedResourceDetails(3L, reservedResourceDetails);
|
||||
assertFalse(reservedResourceDetails.isEmpty());
|
||||
assertEquals(reservedResourceDetails.size(), entry.getValue().size());
|
||||
assertTrue(reservedResourceDetails.containsAll(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,13 @@
|
|||
package com.cloud.api.query.dao;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
|
@ -34,8 +33,6 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.gpu.dao.VgpuProfileDao;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
|
|
@ -49,6 +46,7 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
|||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VnfNicResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.apache.cloudstack.vm.lease.VMLeaseManager;
|
||||
|
|
@ -61,11 +59,13 @@ import com.cloud.api.ApiDBUtils;
|
|||
import com.cloud.api.ApiResponseHelper;
|
||||
import com.cloud.api.query.vo.UserVmJoinVO;
|
||||
import com.cloud.gpu.GPU;
|
||||
import com.cloud.gpu.dao.VgpuProfileDao;
|
||||
import com.cloud.host.ControlState;
|
||||
import com.cloud.network.IpAddress;
|
||||
import com.cloud.network.vpc.VpcVO;
|
||||
import com.cloud.network.vpc.dao.VpcDao;
|
||||
import com.cloud.service.ServiceOfferingDetailsVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.storage.Storage.TemplateType;
|
||||
|
|
@ -92,7 +92,6 @@ import com.cloud.vm.VirtualMachine.State;
|
|||
import com.cloud.vm.VmStats;
|
||||
import com.cloud.vm.dao.NicExtraDhcpOptionDao;
|
||||
import com.cloud.vm.dao.NicSecondaryIpVO;
|
||||
|
||||
import com.cloud.vm.dao.VMInstanceDetailsDao;
|
||||
|
||||
@Component
|
||||
|
|
@ -124,6 +123,8 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
|
|||
private ServiceOfferingDao serviceOfferingDao;
|
||||
@Inject
|
||||
private VgpuProfileDao vgpuProfileDao;
|
||||
@Inject
|
||||
ExtensionHelper extensionHelper;
|
||||
|
||||
private final SearchBuilder<UserVmJoinVO> VmDetailSearch;
|
||||
private final SearchBuilder<UserVmJoinVO> activeVmByIsoSearch;
|
||||
|
|
@ -456,7 +457,16 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
|
|||
|
||||
// Remove deny listed settings if user is not admin
|
||||
if (caller.getType() != Account.Type.ADMIN) {
|
||||
String[] userVmSettingsToHide = QueryService.UserVMDeniedDetails.value().split(",");
|
||||
List<String> userVmSettingsToHide = new ArrayList<>();
|
||||
String[] parts = QueryService.UserVMDeniedDetails.value().split(",");
|
||||
if (parts.length > 0) {
|
||||
Collections.addAll(userVmSettingsToHide, parts);
|
||||
}
|
||||
if (userVm.getTemplateExtensionId() != null) {
|
||||
userVmSettingsToHide.addAll(extensionHelper.getExtensionReservedResourceDetails(
|
||||
userVm.getTemplateExtensionId()));
|
||||
}
|
||||
|
||||
for (String key : userVmSettingsToHide) {
|
||||
resourceDetails.remove(key.trim());
|
||||
}
|
||||
|
|
@ -512,7 +522,6 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
|
|||
return userVmResponse;
|
||||
}
|
||||
|
||||
|
||||
private long computeLeaseDurationFromExpiryDate(Date created, Date leaseExpiryDate) {
|
||||
LocalDate createdDate = created.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
LocalDate expiryDate = leaseExpiryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
|
|||
@Column(name = "template_format")
|
||||
private Storage.ImageFormat templateFormat;
|
||||
|
||||
@Column(name = "template_extension_id")
|
||||
private Long templateExtensionId;
|
||||
|
||||
@Column(name = "password_enabled")
|
||||
private boolean passwordEnabled;
|
||||
|
||||
|
|
@ -709,6 +712,10 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
|
|||
return templateFormat;
|
||||
}
|
||||
|
||||
public Long getTemplateExtensionId() {
|
||||
return templateExtensionId;
|
||||
}
|
||||
|
||||
public boolean isPasswordEnabled() {
|
||||
return passwordEnabled;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
|
|||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
|
|
@ -632,6 +633,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
@Inject
|
||||
SnapshotDataFactory snapshotDataFactory;
|
||||
|
||||
@Inject
|
||||
ExtensionHelper extensionHelper;
|
||||
|
||||
private ScheduledExecutorService _executor = null;
|
||||
private ScheduledExecutorService _vmIpFetchExecutor = null;
|
||||
private int _expungeInterval;
|
||||
|
|
@ -2907,6 +2911,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
.map(item -> (item).trim())
|
||||
.collect(Collectors.toList());
|
||||
userDenyListedSettings.addAll(QueryService.RootAdminOnlyVmSettings);
|
||||
if (template != null && template.getExtensionId() != null) {
|
||||
userDenyListedSettings.addAll(extensionHelper.getExtensionReservedResourceDetails(
|
||||
template.getExtensionId()));
|
||||
}
|
||||
|
||||
final List<String> userReadOnlySettings = Stream.of(QueryService.UserVMReadOnlyDetails.value().split(","))
|
||||
.map(item -> (item).trim())
|
||||
.collect(Collectors.toList());
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
|||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.extension.ExtensionHelper;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
|
@ -78,6 +79,9 @@ public class UserVmJoinDaoImplTest extends GenericDaoBaseWithTagInformationBaseT
|
|||
@Mock
|
||||
private VnfTemplateDetailsDao vnfTemplateDetailsDao;
|
||||
|
||||
@Mock
|
||||
ExtensionHelper extensionHelper;
|
||||
|
||||
private UserVmJoinVO userVm = new UserVmJoinVO();
|
||||
private UserVmResponse userVmResponse = new UserVmResponse();
|
||||
|
||||
|
|
|
|||
|
|
@ -2087,6 +2087,7 @@
|
|||
"label.requireshvm": "HVM",
|
||||
"label.requiresupgrade": "Requires upgrade",
|
||||
"label.reserved": "Reserved",
|
||||
"label.reservedresourcedetails": "Reserved resource details",
|
||||
"label.reserved.system.gateway": "Reserved system gateway",
|
||||
"label.reserved.system.ip": "Reserved system IP",
|
||||
"label.reserved.system.netmask": "Reserved system netmask",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
}, 'created']
|
||||
return fields
|
||||
},
|
||||
details: ['name', 'description', 'id', 'type', 'details', 'path', 'pathready', 'isuserdefined', 'orchestratorrequirespreparevm', 'created'],
|
||||
details: ['name', 'description', 'id', 'type', 'details', 'path', 'pathready', 'isuserdefined', 'orchestratorrequirespreparevm', 'reservedresourcedetails', 'created'],
|
||||
filters: ['orchestrator'],
|
||||
tabs: [{
|
||||
name: 'details',
|
||||
|
|
|
|||
|
|
@ -89,6 +89,14 @@
|
|||
<details-input
|
||||
v-model:value="form.details" />
|
||||
</a-form-item>
|
||||
<a-form-item name="reservedresourcedetails" ref="reservedresourcedetails">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.reservedresourcedetails')" :tooltip="apiParams.reservedresourcedetails.description"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.reservedresourcedetails"
|
||||
:placeholder="apiParams.reservedresourcedetails.description" />
|
||||
</a-form-item>
|
||||
<a-form-item name="state" ref="state">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.enabled')" :tooltip="apiParams.state.description"/>
|
||||
|
|
@ -201,6 +209,9 @@ export default {
|
|||
params['details[0].' + key] = value
|
||||
})
|
||||
}
|
||||
if (values.reservedresourcedetails) {
|
||||
params.reservedresourcedetails = values.reservedresourcedetails
|
||||
}
|
||||
postAPI('createExtension', params).then(response => {
|
||||
this.$emit('refresh-data')
|
||||
this.$notification.success({
|
||||
|
|
|
|||
|
|
@ -46,6 +46,14 @@
|
|||
<details-input
|
||||
v-model:value="form.details" />
|
||||
</a-form-item>
|
||||
<a-form-item name="reservedresourcedetails" ref="reservedresourcedetails">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.reservedresourcedetails')" :tooltip="apiParams.reservedresourcedetails.description"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.reservedresourcedetails"
|
||||
:placeholder="apiParams.reservedresourcedetails.description" />
|
||||
</a-form-item>
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||
|
|
@ -90,13 +98,16 @@ export default {
|
|||
this.form = reactive({
|
||||
description: this.resource.description,
|
||||
details: this.resource.details,
|
||||
orchestratorrequirespreparevm: this.resource.orchestratorrequirespreparevm
|
||||
orchestratorrequirespreparevm: this.resource.orchestratorrequirespreparevm,
|
||||
reservedresourcedetails: this.resource.reservedresourcedetails
|
||||
})
|
||||
},
|
||||
fetchData () {
|
||||
this.loading = true
|
||||
getAPI('listExtensions', { id: this.resource.id }).then(json => {
|
||||
this.form.details = json?.listextensionsresponse?.extension?.[0]?.details
|
||||
const ext = json?.listextensionsresponse?.extension?.[0] || {}
|
||||
this.form.details = ext.details
|
||||
this.form.reservedresourcedetails = ext.reservedresourcedetails
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
|
|
@ -110,7 +121,7 @@ export default {
|
|||
const params = {
|
||||
id: this.resource.id
|
||||
}
|
||||
const keys = ['description', 'orchestratorrequirespreparevm']
|
||||
const keys = ['description', 'orchestratorrequirespreparevm', 'reservedresourcedetails']
|
||||
for (const key of keys) {
|
||||
if (values[key] !== undefined || values[key] !== null) {
|
||||
params[key] = values[key]
|
||||
|
|
|
|||
Loading…
Reference in New Issue