Add offerHa and forceHa preset variables to RUNNING_VM usage type (#9500)

* Add offerHA and forceHa presets to Running VM

* apply winterhazel's suggestion

Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>

* Add ObjectUtils import

---------

Co-authored-by: Lucas Martins <lucas.martins@scclouds.com.br>
Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>
This commit is contained in:
Lucas Martins 2025-02-14 08:32:51 -03:00 committed by GitHub
parent a093f00ab5
commit 617fee8416
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 180 additions and 5 deletions

View File

@ -34,6 +34,7 @@ import javax.naming.ConfigurationException;
import com.cloud.user.Account;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.activationrule.presetvariables.Configuration;
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
@ -467,6 +468,11 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
}
Configuration configuration = presetVariables.getConfiguration();
if (configuration != null) {
jsInterpreter.injectVariable("configuration", configuration.toString());
}
jsInterpreter.injectStringVariable("resourceType", presetVariables.getResourceType());
jsInterpreter.injectVariable("value", presetVariables.getValue().toString());
jsInterpreter.injectVariable("zone", presetVariables.getZone().toString());

View File

@ -17,10 +17,15 @@
package org.apache.cloudstack.quota.activationrule.presetvariables;
import org.apache.cloudstack.quota.constant.QuotaTypes;
public class ComputeOffering extends GenericPresetVariable {
@PresetVariableDefinition(description = "A boolean informing if the compute offering is customized or not.")
private boolean customized;
@PresetVariableDefinition(description = "A boolean informing if the compute offering offers HA or not.", supportedTypes = {QuotaTypes.RUNNING_VM})
private boolean offerHa;
public boolean isCustomized() {
return customized;
}
@ -30,4 +35,13 @@ public class ComputeOffering extends GenericPresetVariable {
fieldNamesToIncludeInToString.add("customized");
}
public boolean offerHa() {
return offerHa;
}
public void setOfferHa(boolean offerHa) {
this.offerHa = offerHa;
fieldNamesToIncludeInToString.add("offerHa");
}
}

View File

@ -0,0 +1,35 @@
// 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.quota.activationrule.presetvariables;
import org.apache.cloudstack.quota.constant.QuotaTypes;
public class Configuration extends GenericPresetVariable{
@PresetVariableDefinition(description = "A boolean informing if the cluster configuration force.ha is enabled or not.", supportedTypes = {QuotaTypes.RUNNING_VM})
private boolean forceHa;
public boolean getForceHa() {
return forceHa;
}
public void setForceHa(boolean forceHa) {
this.forceHa = forceHa;
fieldNamesToIncludeInToString.add("forceHa");
}
}

View File

@ -25,6 +25,8 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterDetailsVO;
import com.cloud.host.HostTagVO;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.vpc.VpcVO;
@ -37,6 +39,7 @@ import org.apache.cloudstack.acl.dao.RoleDao;
import org.apache.cloudstack.backup.BackupOfferingVO;
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.NetworkDao;
import org.apache.cloudstack.quota.dao.VmTemplateDao;
@ -51,6 +54,7 @@ import org.apache.cloudstack.usage.UsageTypes;
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.springframework.stereotype.Component;
@ -181,6 +185,11 @@ public class PresetVariableHelper {
@Inject
VpcDao vpcDao;
@Inject
ConfigurationDao configDao;
@Inject
ClusterDetailsDao clusterDetailsDao;
protected boolean backupSnapshotAfterTakingSnapshot = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
@ -194,6 +203,7 @@ public class PresetVariableHelper {
presetVariables.setAccount(getPresetVariableAccount(usageRecord.getAccountId()));
setPresetVariableProject(presetVariables);
setPresetVariableConfiguration(presetVariables, usageRecord);
presetVariables.setDomain(getPresetVariableDomain(usageRecord.getDomainId()));
presetVariables.setResourceType(usageRecord.getType());
@ -272,6 +282,39 @@ public class PresetVariableHelper {
return zone;
}
protected void setPresetVariableConfiguration(PresetVariables presetVariables, UsageVO usageRecord) {
if (usageRecord.getUsageType() != UsageTypes.RUNNING_VM) {
return;
}
Configuration configuration = new Configuration();
setForceHaInConfiguration(configuration, usageRecord);
presetVariables.setConfiguration(configuration);
}
protected void setForceHaInConfiguration(Configuration configuration, UsageVO usageRecord) {
Long vmId = usageRecord.getUsageId();
VMInstanceVO vmVo = vmInstanceDao.findByIdIncludingRemoved(vmId);
validateIfObjectIsNull(vmVo, vmId, "VM");
Long hostId = ObjectUtils.defaultIfNull(vmVo.getHostId(), vmVo.getLastHostId());
HostVO hostVo = hostDao.findByIdIncludingRemoved(hostId);
validateIfObjectIsNull(hostVo, hostId, "host");
ClusterDetailsVO forceHa = clusterDetailsDao.findDetail(hostVo.getClusterId(), "force.ha");
String forceHaValue;
if (forceHa != null) {
forceHaValue = forceHa.getValue();
} else {
forceHaValue = configDao.getValue("force.ha");
}
configuration.setForceHa((Boolean.parseBoolean(forceHaValue)));
}
protected Value getPresetVariableValue(UsageVO usageRecord) {
Long accountId = usageRecord.getAccountId();
int usageType = usageRecord.getUsageType();
@ -390,12 +433,16 @@ public class PresetVariableHelper {
return guestOsVo.getDisplayName();
}
protected ComputeOffering getPresetVariableValueComputeOffering(ServiceOfferingVO serviceOfferingVo) {
protected ComputeOffering getPresetVariableValueComputeOffering(ServiceOfferingVO serviceOfferingVo, int usageType) {
ComputeOffering computeOffering = new ComputeOffering();
computeOffering.setId(serviceOfferingVo.getUuid());
computeOffering.setName(serviceOfferingVo.getName());
computeOffering.setCustomized(serviceOfferingVo.isDynamic());
if (usageType == UsageTypes.RUNNING_VM) {
computeOffering.setOfferHa(serviceOfferingVo.isOfferHA());
}
return computeOffering;
}
@ -404,7 +451,7 @@ public class PresetVariableHelper {
long computeOfferingId = vmVo.getServiceOfferingId();
ServiceOfferingVO serviceOfferingVo = serviceOfferingDao.findByIdIncludingRemoved(computeOfferingId);
validateIfObjectIsNull(serviceOfferingVo, computeOfferingId, "compute offering");
value.setComputeOffering(getPresetVariableValueComputeOffering(serviceOfferingVo));
value.setComputeOffering(getPresetVariableValueComputeOffering(serviceOfferingVo, usageType));
if (usageType == UsageTypes.RUNNING_VM) {
value.setComputingResources(getPresetVariableValueComputingResource(vmVo, serviceOfferingVo));

View File

@ -39,6 +39,9 @@ public class PresetVariables {
@PresetVariableDefinition(description = "Zone where the resource is.")
private GenericPresetVariable zone;
@PresetVariableDefinition(description = "Configurations of the resource.")
private Configuration configuration;
@PresetVariableDefinition(description = "A list containing the tariffs ordered by the field 'position'.")
private List<Tariff> lastTariffs;
@ -90,6 +93,14 @@ public class PresetVariables {
this.zone = zone;
}
public Configuration getConfiguration() {
return configuration;
}
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
public List<Tariff> getLastTariffs() {
return lastTariffs;
}

View File

@ -270,6 +270,7 @@ public class QuotaManagerImplTest {
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("account"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("domain"), Mockito.anyString());
Mockito.verify(jsInterpreterMock, Mockito.never()).injectVariable(Mockito.eq("project"), Mockito.anyString());
Mockito.verify(jsInterpreterMock, Mockito.never()).injectVariable(Mockito.eq("configuration"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectStringVariable(Mockito.eq("resourceType"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("value"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("zone"), Mockito.anyString());

View File

@ -27,6 +27,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterDetailsVO;
import com.cloud.host.HostTagVO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.StoragePoolTagVO;
@ -123,6 +125,9 @@ public class PresetVariableHelperTest {
@Mock
HostTagsDao hostTagsDaoMock;
@Mock
ClusterDetailsDao clusterDetailsDaoMock;
@Mock
ImageStoreDao imageStoreDaoMock;
@ -234,6 +239,7 @@ public class PresetVariableHelperTest {
computeOffering.setId("compute_offering_id");
computeOffering.setName("compute_offering_name");
computeOffering.setCustomized(false);
computeOffering.setOfferHa(false);
return computeOffering;
}
@ -245,6 +251,14 @@ public class PresetVariableHelperTest {
return host;
}
private Configuration getConfigurationForTests() {
Configuration configuration = new Configuration();
configuration.setId("config_id");
configuration.setName("config_name");
configuration.setForceHa(false);
return configuration;
}
private List<HostTagVO> getHostTagsForTests() {
return Arrays.asList(new HostTagVO(1, "tag1", false), new HostTagVO(1, "tag2", false));
}
@ -338,6 +352,7 @@ public class PresetVariableHelperTest {
Mockito.doReturn(expected.getAccount()).when(presetVariableHelperSpy).getPresetVariableAccount(Mockito.anyLong());
Mockito.doNothing().when(presetVariableHelperSpy).setPresetVariableProject(Mockito.any());
Mockito.doNothing().when(presetVariableHelperSpy).setPresetVariableConfiguration(Mockito.any(), Mockito.any());
Mockito.doReturn(expected.getDomain()).when(presetVariableHelperSpy).getPresetVariableDomain(Mockito.anyLong());
Mockito.doReturn(expected.getValue()).when(presetVariableHelperSpy).getPresetVariableValue(Mockito.any(UsageVO.class));
Mockito.doReturn(expected.getZone()).when(presetVariableHelperSpy).getPresetVariableZone(Mockito.anyLong());
@ -361,6 +376,35 @@ public class PresetVariableHelperTest {
Assert.assertNull(result.getProject());
}
@Test
public void setPresetVariableConfigurationTestQuotaTypeDifferentFromRunningVmDoNothing() {
getQuotaTypesForTests(UsageTypes.RUNNING_VM).forEach(type -> {
PresetVariables result = new PresetVariables();
Mockito.doReturn(type.getKey()).when(usageVoMock).getUsageType();
presetVariableHelperSpy.setPresetVariableConfiguration(result, usageVoMock);
Assert.assertNull(result.getConfiguration());
});
}
@Test
public void setPresetVariableConfigurationTestQuotaTypeIsRunningVmSetConfiguration() {
PresetVariables result = new PresetVariables();
Configuration expectedConfig = getConfigurationForTests();
HostVO hostVoMock = Mockito.mock(HostVO.class);
ClusterDetailsVO clusterDetailsVoMock = Mockito.mock(ClusterDetailsVO.class);
Mockito.doReturn(vmInstanceVoMock).when(vmInstanceDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
Mockito.doReturn(hostVoMock).when(hostDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
Mockito.doReturn(1L).when(vmInstanceVoMock).getHostId();
Mockito.doReturn(1).when(usageVoMock).getUsageType();
Mockito.doReturn(clusterDetailsVoMock).when(clusterDetailsDaoMock).findDetail(Mockito.anyLong(), Mockito.anyString());
presetVariableHelperSpy.setPresetVariableConfiguration(result, usageVoMock);
Assert.assertNotNull(result.getConfiguration());
Assert.assertEquals(expectedConfig.getForceHa(), result.getConfiguration().getForceHa());
}
@Test
public void setPresetVariableProjectTestAccountWithoutRoleSetAsProject() {
PresetVariables result = new PresetVariables();
@ -636,19 +680,36 @@ public class PresetVariableHelperTest {
}
@Test
public void getPresetVariableValueComputeOfferingTestSetFieldsAndReturnObject() {
public void getPresetVariableValueComputeOfferingForTestSetFieldsAndReturnObjectForRunningVm() {
ComputeOffering expected = getComputeOfferingForTests();
Mockito.doReturn(expected.getId()).when(serviceOfferingVoMock).getUuid();
Mockito.doReturn(expected.getName()).when(serviceOfferingVoMock).getName();
Mockito.doReturn(expected.isCustomized()).when(serviceOfferingVoMock).isDynamic();
Mockito.doReturn(expected.offerHa()).when(serviceOfferingVoMock).isOfferHA();
ComputeOffering result = presetVariableHelperSpy.getPresetVariableValueComputeOffering(serviceOfferingVoMock, UsageTypes.RUNNING_VM);
assertPresetVariableIdAndName(expected, result);
Assert.assertEquals(expected.isCustomized(), result.isCustomized());
Assert.assertEquals(expected.offerHa(), result.offerHa());
validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "customized", "offerHa"), result);
}
@Test
public void getPresetVariableValueComputeOfferingForTestSetFieldsAndReturnObjectForAllocatedVm() {
ComputeOffering expected = getComputeOfferingForTests();
Mockito.doReturn(expected.getId()).when(serviceOfferingVoMock).getUuid();
Mockito.doReturn(expected.getName()).when(serviceOfferingVoMock).getName();
Mockito.doReturn(expected.isCustomized()).when(serviceOfferingVoMock).isDynamic();
ComputeOffering result = presetVariableHelperSpy.getPresetVariableValueComputeOffering(serviceOfferingVoMock);
ComputeOffering result = presetVariableHelperSpy.getPresetVariableValueComputeOffering(serviceOfferingVoMock, UsageTypes.ALLOCATED_VM);
assertPresetVariableIdAndName(expected, result);
Assert.assertEquals(expected.isCustomized(), result.isCustomized());
validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "customized"), result);
}
@Test
public void getPresetVariableValueTemplateTestSetValuesAndReturnObject() {
VMTemplateVO vmTemplateVoMock = Mockito.mock(VMTemplateVO.class);
@ -1127,7 +1188,7 @@ public class PresetVariableHelperTest {
Mockito.doReturn(serviceOfferingVoMock).when(serviceOfferingDaoMock).findByIdIncludingRemoved(Mockito.anyLong());
mockMethodValidateIfObjectIsNull();
Mockito.doReturn(expected.getComputeOffering()).when(presetVariableHelperSpy).getPresetVariableValueComputeOffering(Mockito.any());
Mockito.doReturn(expected.getComputeOffering()).when(presetVariableHelperSpy).getPresetVariableValueComputeOffering(Mockito.any(), Mockito.anyInt());
Mockito.doReturn(expected.getComputingResources()).when(presetVariableHelperSpy).getPresetVariableValueComputingResource(Mockito.any(), Mockito.any());
QuotaTypes.listQuotaTypes().forEach((typeInt, value) -> {