Compare commits

...

15 Commits

Author SHA1 Message Date
Vishesh
a839e25d55
Fix OOB test failures in ci.yml github actions (#11887) 2025-10-22 14:50:23 +02:00
dahn
a6068a850c
Update CI workflow to use Ubuntu 24.04 (#11873) 2025-10-21 16:22:46 +02:00
Abhishek Kumar
03a4b9f4fd
server,utils: improve js interpretation functionality
Make JS interpretation functionalities configurable via a hidden config
- js.interpretation.enabled
Default value is false, making such functionalities disabled, ie, new
heuristic rules cannot be added or updated.

For JsInterpretor, use --no-java --no-syntax-extensions args and a deny-all ClassFilter.
Replace string-spliced vars with ENGINE_SCOPE Bindings, use a fresh ScriptContext per run, and compile before eval.
Use a named daemon worker with hard timeouts and capture stdout.

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2025-10-16 09:49:36 +02:00
Abhishek Kumar
c8d44d92a7
api,server: fix entity access
Added access check for:
- createNetworkACL
- listNetworkACLs
- listResourceDetails
- listVirtualMachinesUsageHistory
- listVolumesUsageHistory

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2025-10-16 09:49:34 +02:00
Abhishek Kumar
eee43e534f
cloudutils: fix warning, error during kvm agent installation (#11318)
* cloudutils: fix warning, error during kvm agent installation

Fixes #10379

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* fix

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>

* Update utilities.py

---------

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2025-10-15 20:31:00 +02:00
Wei Zhou
b82369c241
systemvm: fix duplicated "en_US.UTF-8 UTF-8" in /etc/locale.gen (#11823) 2025-10-15 11:42:24 +02:00
Wei Zhou
4327871036
Routed: fix create network exception when auto-allocation is disabled (#11624)
* Routed: fix create network exception when auto-allocation is disabled for regular users

* routed: throw InvalidParameterValueException instead of CloudRuntimeException which gives vague message to regular users
2025-10-14 13:00:33 +02:00
Rohit Yadav
6f931dbd00
agent: increase timeout for host arch retrieval (#11254) (#11822)
Cherry-picked from 44f80648a9ea818e34997416aabbcd95cb03f847

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2025-10-14 10:53:45 +02:00
Wei Zhou
86cad79c15
importvm: fix IP address allocation on Shared networks (#11811) 2025-10-13 08:16:46 +02:00
Abhisar Sinha
4d95f08a3a
Delete template from storage pool instantly if no volume is using it (#11782) 2025-10-09 09:41:18 +02:00
dahn
309b444205
pom.xml: update jetty version (#11793)
* update jetty

* Rollback jetty-maven-plugin version in pom.xml

Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>

---------

Co-authored-by: Daan Hoogland <dahn@apache.org>
Co-authored-by: Wei Zhou <weizhou@apache.org>
Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2025-10-09 08:39:45 +02:00
Wei Zhou
89d2b17461
storage: change storage pool to Up state when cancel storage migration (#11773)
* storage: change storage pool to Up state when cancel storage migration

* Update 11773: connect host to shared pool after cancelling storage migration

* Update 11773: update db only

* Update 11773: skip capacity update for storpool
2025-10-08 15:34:59 +02:00
Suresh Kumar Anaparti
b143ddc405
Sanitize the rbd file cmd parameter logs during qemu-img convert (through Script) (#11801) 2025-10-08 13:55:08 +02:00
Manoj Kumar
9f20979bce
UI: Fix primary storage for datastore cluster and retain traffic labels during zone deployment (#11760) 2025-10-08 13:38:03 +02:00
dahn
270d3f9a2d
UI: Deal with crosssite api call after login (#10533) 2025-10-08 10:42:00 +02:00
47 changed files with 703 additions and 223 deletions

View File

@ -29,7 +29,7 @@ permissions:
jobs: jobs:
build: build:
if: github.repository == 'apache/cloudstack' if: github.repository == 'apache/cloudstack'
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
strategy: strategy:
fail-fast: false fail-fast: false
@ -232,7 +232,25 @@ jobs:
- name: Install Build Dependencies - name: Install Build Dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y git uuid-runtime genisoimage netcat ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 ipmitool libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools sudo apt-get install -y git uuid-runtime genisoimage netcat-openbsd ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 ipmitool libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools
- name: Setup IPMI Tool for CloudStack
run: |
# Create cloudstack-common directory if it doesn't exist
sudo mkdir -p /usr/share/cloudstack-common
# Copy ipmitool to cloudstack-common directory if it doesn't exist
if [ ! -f /usr/share/cloudstack-common/ipmitool ]; then
sudo cp /usr/bin/ipmitool /usr/share/cloudstack-common/ipmitool
sudo chmod 755 /usr/share/cloudstack-common/ipmitool
fi
# Create ipmitool-C3 wrapper script
sudo tee /usr/bin/ipmitool > /dev/null << 'EOF'
#!/bin/bash
/usr/share/cloudstack-common/ipmitool -C3 $@
EOF
sudo chmod 755 /usr/bin/ipmitool
- name: Install Python dependencies - name: Install Python dependencies
run: | run: |
@ -271,7 +289,7 @@ jobs:
- name: Setup Simulator Prerequisites - name: Setup Simulator Prerequisites
run: | run: |
sudo python3 -m pip install --upgrade netaddr mysql-connector-python sudo python3 -m pip install --upgrade netaddr mysql-connector-python
python3 -m pip install --user --upgrade tools/marvin/dist/Marvin-*.tar.gz python3 -m pip install --user --upgrade tools/marvin/dist/[mM]arvin-*.tar.gz
mvn -q -Pdeveloper -pl developer -Ddeploydb mvn -q -Pdeveloper -pl developer -Ddeploydb
mvn -q -Pdeveloper -pl developer -Ddeploydb-simulator mvn -q -Pdeveloper -pl developer -Ddeploydb-simulator

View File

@ -93,7 +93,6 @@ import com.cloud.utils.nio.Link;
import com.cloud.utils.nio.NioClient; import com.cloud.utils.nio.NioClient;
import com.cloud.utils.nio.NioConnection; import com.cloud.utils.nio.NioConnection;
import com.cloud.utils.nio.Task; import com.cloud.utils.nio.Task;
import com.cloud.utils.script.OutputInterpreter;
import com.cloud.utils.script.Script; import com.cloud.utils.script.Script;
/** /**
@ -598,9 +597,9 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater
} }
protected String getAgentArch() { protected String getAgentArch() {
final Script command = new Script("/usr/bin/arch", 500, logger); String arch = Script.runSimpleBashScript(Script.getExecutableAbsolutePath("arch"), 2000);
final OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); logger.debug("Arch for agent: {} found: {}", _name, arch);
return command.execute(parser); return arch;
} }
@Override @Override

View File

@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.cloud.user.UserData;
import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
@ -66,6 +65,7 @@ import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd;
import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd;
import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.config.ConfigurationGroup; import org.apache.cloudstack.config.ConfigurationGroup;
import org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.alert.Alert; import com.cloud.alert.Alert;
import com.cloud.capacity.Capacity; import com.cloud.capacity.Capacity;
@ -85,6 +85,7 @@ import com.cloud.storage.GuestOSHypervisor;
import com.cloud.storage.GuestOsCategory; import com.cloud.storage.GuestOsCategory;
import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePool;
import com.cloud.user.SSHKeyPair; import com.cloud.user.SSHKeyPair;
import com.cloud.user.UserData;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.Ternary; import com.cloud.utils.Ternary;
import com.cloud.vm.InstanceGroup; import com.cloud.vm.InstanceGroup;
@ -98,6 +99,14 @@ import com.cloud.vm.VirtualMachine.Type;
public interface ManagementService { public interface ManagementService {
static final String Name = "management-server"; static final String Name = "management-server";
ConfigKey<Boolean> JsInterpretationEnabled = new ConfigKey<>("Hidden"
, Boolean.class
, "js.interpretation.enabled"
, "false"
, "Enable/Disable all JavaScript interpretation related functionalities to create or update Javascript rules."
, false
, ConfigKey.Scope.Global);
/** /**
* returns the a map of the names/values in the configuration table * returns the a map of the names/values in the configuration table
* *
@ -481,4 +490,6 @@ public interface ManagementService {
Pair<Boolean, String> patchSystemVM(PatchSystemVMCmd cmd); Pair<Boolean, String> patchSystemVM(PatchSystemVMCmd cmd);
void checkJsInterpretationAllowedIfNeededForParameterValue(String paramName, boolean paramValue);
} }

View File

@ -18,6 +18,7 @@ package com.cloud.server;
public interface ResourceManagerUtil { public interface ResourceManagerUtil {
long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType); long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType);
long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType, boolean checkAccess);
String getUuid(String resourceId, ResourceTag.ResourceObjectType resourceType); String getUuid(String resourceId, ResourceTag.ResourceObjectType resourceType);
ResourceTag.ResourceObjectType getResourceType(String resourceTypeStr); ResourceTag.ResourceObjectType getResourceType(String resourceTypeStr);
void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage); void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage);

View File

@ -294,6 +294,8 @@ public interface StorageManager extends StorageService {
Answer sendToPool(StoragePool pool, long[] hostIdsToTryFirst, Command cmd) throws StorageUnavailableException; Answer sendToPool(StoragePool pool, long[] hostIdsToTryFirst, Command cmd) throws StorageUnavailableException;
void updateStoragePoolHostVOAndBytes(StoragePool pool, long hostId, ModifyStoragePoolAnswer mspAnswer);
CapacityVO getSecondaryStorageUsedStats(Long hostId, Long zoneId); CapacityVO getSecondaryStorageUsedStats(Long hostId, Long zoneId);
CapacityVO getStoragePoolUsedStats(Long poolId, Long clusterId, Long podId, Long zoneId); CapacityVO getStoragePoolUsedStats(Long poolId, Long clusterId, Long podId, Long zoneId);

View File

@ -56,6 +56,13 @@ public interface TemplateManager {
+ "will validate if the provided URL is resolvable during the register of templates/ISOs before persisting them in the database.", + "will validate if the provided URL is resolvable during the register of templates/ISOs before persisting them in the database.",
true); true);
ConfigKey<Boolean> TemplateDeleteFromPrimaryStorage = new ConfigKey<Boolean>("Advanced",
Boolean.class,
"template.delete.from.primary.storage", "true",
"Template when deleted will be instantly deleted from the Primary Storage",
true,
ConfigKey.Scope.Global);
static final String VMWARE_TOOLS_ISO = "vmware-tools.iso"; static final String VMWARE_TOOLS_ISO = "vmware-tools.iso";
static final String XS_TOOLS_ISO = "xs-tools.iso"; static final String XS_TOOLS_ISO = "xs-tools.iso";
@ -103,6 +110,8 @@ public interface TemplateManager {
*/ */
List<VMTemplateStoragePoolVO> getUnusedTemplatesInPool(StoragePoolVO pool); List<VMTemplateStoragePoolVO> getUnusedTemplatesInPool(StoragePoolVO pool);
void evictTemplateFromStoragePoolsForZones(Long templateId, List<Long> zoneId);
/** /**
* Deletes a template in the specified storage pool. * Deletes a template in the specified storage pool.
* *

View File

@ -4772,6 +4772,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
} }
}); });
if (selectedIp != null && GuestType.Shared.equals(network.getGuestType())) {
IPAddressVO ipAddressVO = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), selectedIp);
if (ipAddressVO != null && IpAddress.State.Free.equals(ipAddressVO.getState())) {
ipAddressVO.setState(IPAddressVO.State.Allocated);
ipAddressVO.setAllocatedTime(new Date());
Account account = _accountDao.findById(vm.getAccountId());
ipAddressVO.setAllocatedInDomainId(account.getDomainId());
ipAddressVO.setAllocatedToAccountId(account.getId());
_ipAddressDao.update(ipAddressVO.getId(), ipAddressVO);
}
}
final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId());
final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network),
_networkModel.getNetworkTag(vm.getHypervisorType(), network)); _networkModel.getNetworkTag(vm.getHypervisorType(), network));
@ -4783,15 +4795,15 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
if (network.getGuestType() == GuestType.L2) { if (network.getGuestType() == GuestType.L2) {
return null; return null;
} }
return dataCenter.getNetworkType() == NetworkType.Basic ? return GuestType.Shared.equals(network.getGuestType()) ?
getSelectedIpForNicImportOnBasicZone(ipAddresses.getIp4Address(), network, dataCenter): getSelectedIpForNicImportOnSharedNetwork(ipAddresses.getIp4Address(), network, dataCenter):
_ipAddrMgr.acquireGuestIpAddress(network, ipAddresses.getIp4Address()); _ipAddrMgr.acquireGuestIpAddress(network, ipAddresses.getIp4Address());
} }
protected String getSelectedIpForNicImportOnBasicZone(String requestedIp, Network network, DataCenter dataCenter) { protected String getSelectedIpForNicImportOnSharedNetwork(String requestedIp, Network network, DataCenter dataCenter) {
IPAddressVO ipAddressVO = StringUtils.isBlank(requestedIp) ? IPAddressVO ipAddressVO = StringUtils.isBlank(requestedIp) ?
_ipAddressDao.findBySourceNetworkIdAndDatacenterIdAndState(network.getId(), dataCenter.getId(), IpAddress.State.Free): _ipAddressDao.findBySourceNetworkIdAndDatacenterIdAndState(network.getId(), dataCenter.getId(), IpAddress.State.Free):
_ipAddressDao.findByIp(requestedIp); _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), requestedIp);
if (ipAddressVO == null || ipAddressVO.getState() != IpAddress.State.Free) { if (ipAddressVO == null || ipAddressVO.getState() != IpAddress.State.Free) {
String msg = String.format("Cannot find a free IP to assign to VM NIC on network %s", network.getName()); String msg = String.format("Cannot find a free IP to assign to VM NIC on network %s", network.getName());
logger.error(msg); logger.error(msg);

View File

@ -822,7 +822,7 @@ public class NetworkOrchestratorTest extends TestCase {
Mockito.when(network.getId()).thenReturn(networkId); Mockito.when(network.getId()).thenReturn(networkId);
Mockito.when(dataCenter.getId()).thenReturn(dataCenterId); Mockito.when(dataCenter.getId()).thenReturn(dataCenterId);
Mockito.when(ipAddresses.getIp4Address()).thenReturn(requestedIp); Mockito.when(ipAddresses.getIp4Address()).thenReturn(requestedIp);
Mockito.when(testOrchestrator._ipAddressDao.findByIp(requestedIp)).thenReturn(ipAddressVO); Mockito.when(testOrchestrator._ipAddressDao.findByIpAndSourceNetworkId(networkId, requestedIp)).thenReturn(ipAddressVO);
String ipAddress = testOrchestrator.getSelectedIpForNicImport(network, dataCenter, ipAddresses); String ipAddress = testOrchestrator.getSelectedIpForNicImport(network, dataCenter, ipAddresses);
Assert.assertEquals(requestedIp, ipAddress); Assert.assertEquals(requestedIp, ipAddress);
} }

View File

@ -35,6 +35,8 @@ public interface VMTemplatePoolDao extends GenericDao<VMTemplateStoragePoolVO, L
List<VMTemplateStoragePoolVO> listByPoolIdAndState(long poolId, ObjectInDataStoreStateMachine.State state); List<VMTemplateStoragePoolVO> listByPoolIdAndState(long poolId, ObjectInDataStoreStateMachine.State state);
List<VMTemplateStoragePoolVO> listByPoolIdsAndTemplate(List<Long> poolIds, Long templateId);
List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState); List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState);
List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState, long poolId); List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState, long poolId);

View File

@ -150,6 +150,16 @@ public class VMTemplatePoolDaoImpl extends GenericDaoBase<VMTemplateStoragePoolV
return findOneIncludingRemovedBy(sc); return findOneIncludingRemovedBy(sc);
} }
@Override
public List<VMTemplateStoragePoolVO> listByPoolIdsAndTemplate(List<Long> poolIds, Long templateId) {
SearchCriteria<VMTemplateStoragePoolVO> sc = PoolTemplateSearch.create();
if (CollectionUtils.isNotEmpty(poolIds)) {
sc.setParameters("pool_id", poolIds.toArray());
}
sc.setParameters("template_id", templateId);
return listBy(sc);
}
@Override @Override
public List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState) { public List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState) {
SearchCriteria<VMTemplateStoragePoolVO> sc = TemplateStatusSearch.create(); SearchCriteria<VMTemplateStoragePoolVO> sc = TemplateStatusSearch.create();

View File

@ -154,4 +154,6 @@ public interface PrimaryDataStoreDao extends GenericDao<StoragePoolVO, Long> {
String keyword, Filter searchFilter); String keyword, Filter searchFilter);
List<StoragePoolVO> listByIds(List<Long> ids); List<StoragePoolVO> listByIds(List<Long> ids);
List<StoragePoolVO> listByDataCenterIds(List<Long> dataCenterIds);
} }

View File

@ -63,6 +63,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
private final GenericSearchBuilder<StoragePoolVO, Long> StatusCountSearch; private final GenericSearchBuilder<StoragePoolVO, Long> StatusCountSearch;
private final SearchBuilder<StoragePoolVO> ClustersSearch; private final SearchBuilder<StoragePoolVO> ClustersSearch;
private final SearchBuilder<StoragePoolVO> IdsSearch; private final SearchBuilder<StoragePoolVO> IdsSearch;
private final SearchBuilder<StoragePoolVO> DcsSearch;
@Inject @Inject
private StoragePoolDetailsDao _detailsDao; private StoragePoolDetailsDao _detailsDao;
@ -155,6 +156,9 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
IdsSearch.and("ids", IdsSearch.entity().getId(), SearchCriteria.Op.IN); IdsSearch.and("ids", IdsSearch.entity().getId(), SearchCriteria.Op.IN);
IdsSearch.done(); IdsSearch.done();
DcsSearch = createSearchBuilder();
DcsSearch.and("dataCenterId", DcsSearch.entity().getDataCenterId(), SearchCriteria.Op.IN);
DcsSearch.done();
} }
@Override @Override
@ -733,6 +737,16 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
return listBy(sc); return listBy(sc);
} }
@Override
public List<StoragePoolVO> listByDataCenterIds(List<Long> dataCenterIds) {
if (CollectionUtils.isEmpty(dataCenterIds)) {
return Collections.emptyList();
}
SearchCriteria<StoragePoolVO> sc = DcsSearch.create();
sc.setParameters("dataCenterId", dataCenterIds.toArray());
return listBy(sc);
}
private SearchCriteria<StoragePoolVO> createStoragePoolSearchCriteria(Long storagePoolId, String storagePoolName, private SearchCriteria<StoragePoolVO> createStoragePoolSearchCriteria(Long storagePoolId, String storagePoolName,
Long zoneId, String path, Long podId, Long clusterId, String address, ScopeType scopeType, Long zoneId, String path, Long podId, Long clusterId, String address, ScopeType scopeType,
StoragePoolStatus status, String keyword) { StoragePoolStatus status, String keyword) {

View File

@ -32,7 +32,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import com.cloud.user.Account;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable; import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper; import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
@ -62,6 +61,7 @@ import org.springframework.stereotype.Component;
import com.cloud.usage.UsageVO; import com.cloud.usage.UsageVO;
import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageDao;
import com.cloud.user.Account;
import com.cloud.user.AccountVO; import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
@ -467,7 +467,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
} }
jsInterpreter.injectStringVariable("resourceType", presetVariables.getResourceType()); jsInterpreter.injectVariable("resourceType", presetVariables.getResourceType());
jsInterpreter.injectVariable("value", presetVariables.getValue().toString()); jsInterpreter.injectVariable("value", presetVariables.getValue().toString());
jsInterpreter.injectVariable("zone", presetVariables.getZone().toString()); jsInterpreter.injectVariable("zone", presetVariables.getZone().toString());
} }

View File

@ -270,7 +270,7 @@ public class QuotaManagerImplTest {
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("account"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("account"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("domain"), 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("project"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectStringVariable(Mockito.eq("resourceType"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("resourceType"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("value"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("value"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("zone"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("zone"), Mockito.anyString());
} }
@ -291,7 +291,7 @@ public class QuotaManagerImplTest {
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("account"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("account"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("domain"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("domain"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("project"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("project"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectStringVariable(Mockito.eq("resourceType"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("resourceType"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("value"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("value"), Mockito.anyString());
Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("zone"), Mockito.anyString()); Mockito.verify(jsInterpreterMock).injectVariable(Mockito.eq("zone"), Mockito.anyString());
} }

View File

@ -39,7 +39,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import com.cloud.utils.DateUtil;
import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.QuotaBalanceCmd; import org.apache.cloudstack.api.command.QuotaBalanceCmd;
@ -70,8 +69,8 @@ import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao; import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao; import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao; import org.apache.cloudstack.quota.dao.QuotaTariffDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.dao.QuotaUsageDao; import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO; import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaCreditsVO; import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO; import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
@ -79,26 +78,28 @@ import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO; import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO; import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.cloud.domain.DomainVO; import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO; import com.cloud.user.AccountVO;
import com.cloud.user.User; import com.cloud.user.User;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserDao;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter; import com.cloud.utils.db.Filter;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
@Component @Component
public class QuotaResponseBuilderImpl implements QuotaResponseBuilder { public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
@ -139,6 +140,12 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
@Inject @Inject
private ApiDiscoveryService apiDiscoveryService; private ApiDiscoveryService apiDiscoveryService;
protected void checkActivationRulesAllowed(String activationRule) {
if (!_quotaService.isJsInterpretationEnabled() && StringUtils.isNotEmpty(activationRule)) {
throw new PermissionDeniedException("Quota Tariff Activation Rule cannot be set, as Javascript interpretation is disabled in the configuration.");
}
}
@Override @Override
public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff, boolean returnActivationRule) { public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff, boolean returnActivationRule) {
final QuotaTariffResponse response = new QuotaTariffResponse(); final QuotaTariffResponse response = new QuotaTariffResponse();
@ -440,6 +447,8 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
throw new InvalidParameterValueException(String.format("There is no quota tariffs with name [%s].", name)); throw new InvalidParameterValueException(String.format("There is no quota tariffs with name [%s].", name));
} }
checkActivationRulesAllowed(activationRule);
Date currentQuotaTariffStartDate = currentQuotaTariff.getEffectiveOn(); Date currentQuotaTariffStartDate = currentQuotaTariff.getEffectiveOn();
currentQuotaTariff.setRemoved(now); currentQuotaTariff.setRemoved(now);
@ -696,6 +705,8 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
throw new InvalidParameterValueException(String.format("A quota tariff with name [%s] already exist.", name)); throw new InvalidParameterValueException(String.format("A quota tariff with name [%s] already exist.", name));
} }
checkActivationRulesAllowed(activationRule);
if (startDate.compareTo(now) < 0) { if (startDate.compareTo(now) < 0) {
throw new InvalidParameterValueException(String.format("The value passed as Quota tariff's start date is in the past: [%s]. " + throw new InvalidParameterValueException(String.format("The value passed as Quota tariff's start date is in the past: [%s]. " +
"Please, inform a date in the future or do not pass the parameter to use the current date and time.", startDate)); "Please, inform a date in the future or do not pass the parameter to use the current date and time.", startDate));

View File

@ -16,15 +16,15 @@
//under the License. //under the License.
package org.apache.cloudstack.quota; package org.apache.cloudstack.quota;
import com.cloud.user.AccountVO; import java.math.BigDecimal;
import com.cloud.utils.component.PluggableService; import java.util.Date;
import java.util.List;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO; import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO; import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import java.math.BigDecimal; import com.cloud.user.AccountVO;
import java.util.Date; import com.cloud.utils.component.PluggableService;
import java.util.List;
public interface QuotaService extends PluggableService { public interface QuotaService extends PluggableService {
@ -40,4 +40,6 @@ public interface QuotaService extends PluggableService {
boolean saveQuotaAccount(AccountVO account, BigDecimal aggrUsage, Date endDate); boolean saveQuotaAccount(AccountVO account, BigDecimal aggrUsage, Date endDate);
boolean isJsInterpretationEnabled();
} }

View File

@ -60,6 +60,7 @@ import com.cloud.configuration.Config;
import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.PermissionDeniedException;
import com.cloud.server.ManagementService;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountVO; import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
@ -86,6 +87,8 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
private TimeZone _usageTimezone; private TimeZone _usageTimezone;
private boolean jsInterpretationEnabled = false;
public QuotaServiceImpl() { public QuotaServiceImpl() {
super(); super();
} }
@ -97,6 +100,8 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
String timeZoneStr = ObjectUtils.defaultIfNull(_configDao.getValue(Config.UsageAggregationTimezone.toString()), "GMT"); String timeZoneStr = ObjectUtils.defaultIfNull(_configDao.getValue(Config.UsageAggregationTimezone.toString()), "GMT");
_usageTimezone = TimeZone.getTimeZone(timeZoneStr); _usageTimezone = TimeZone.getTimeZone(timeZoneStr);
jsInterpretationEnabled = ManagementService.JsInterpretationEnabled.value();
return true; return true;
} }
@ -284,4 +289,8 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
} }
} }
@Override
public boolean isJsInterpretationEnabled() {
return jsInterpretationEnabled;
}
} }

View File

@ -58,7 +58,7 @@ public class KVMHostInfo {
private long reservedMemory; private long reservedMemory;
private long overCommitMemory; private long overCommitMemory;
private List<String> capabilities = new ArrayList<>(); private List<String> capabilities = new ArrayList<>();
private static String cpuArchCommand = "/usr/bin/arch"; private static String cpuArchRetrieveExecutable = "arch";
private static List<String> cpuInfoFreqFileNames = List.of("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency","/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"); private static List<String> cpuInfoFreqFileNames = List.of("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency","/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
public KVMHostInfo(long reservedMemory, long overCommitMemory, long manualSpeed, int reservedCpus) { public KVMHostInfo(long reservedMemory, long overCommitMemory, long manualSpeed, int reservedCpus) {
@ -248,6 +248,6 @@ public class KVMHostInfo {
private String getCPUArchFromCommand() { private String getCPUArchFromCommand() {
LOGGER.info("Fetching host CPU arch"); LOGGER.info("Fetching host CPU arch");
return Script.runSimpleBashScript(cpuArchCommand); return Script.runSimpleBashScript(Script.getExecutableAbsolutePath(cpuArchRetrieveExecutable));
} }
} }

View File

@ -22,6 +22,7 @@ import static com.cloud.utils.NumbersUtil.toReadableSize;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -32,8 +33,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import com.cloud.dc.ClusterVO;
import com.cloud.utils.Ternary;
import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ListClustersMetricsCmd; import org.apache.cloudstack.api.ListClustersMetricsCmd;
import org.apache.cloudstack.api.ListDbMetricsCmd; import org.apache.cloudstack.api.ListDbMetricsCmd;
@ -100,6 +99,7 @@ import com.cloud.capacity.CapacityManager;
import com.cloud.capacity.dao.CapacityDao; import com.cloud.capacity.dao.CapacityDao;
import com.cloud.capacity.dao.CapacityDaoImpl; import com.cloud.capacity.dao.CapacityDaoImpl;
import com.cloud.cluster.dao.ManagementServerHostDao; import com.cloud.cluster.dao.ManagementServerHostDao;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter;
import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterDao;
@ -112,6 +112,7 @@ import com.cloud.host.Status;
import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDao;
import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter;
import com.cloud.org.Cluster; import com.cloud.org.Cluster;
import com.cloud.projects.Project;
import com.cloud.server.DbStatsCollection; import com.cloud.server.DbStatsCollection;
import com.cloud.server.ManagementServerHostStats; import com.cloud.server.ManagementServerHostStats;
import com.cloud.server.StatsCollector; import com.cloud.server.StatsCollector;
@ -124,6 +125,7 @@ import com.cloud.usage.dao.UsageJobDao;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.db.DbProperties; import com.cloud.utils.db.DbProperties;
import com.cloud.utils.db.DbUtil; import com.cloud.utils.db.DbUtil;
import com.cloud.utils.db.Filter; import com.cloud.utils.db.Filter;
@ -184,6 +186,10 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements
private static Gson gson = new Gson(); private static Gson gson = new Gson();
private final List<Account.Type> AccountTypesWithRecursiveUsageAccess = Arrays.asList(
Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN, Account.Type.READ_ONLY_ADMIN
);
protected MetricsServiceImpl() { protected MetricsServiceImpl() {
super(); super();
} }
@ -245,17 +251,30 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements
* @return the list of VMs. * @return the list of VMs.
*/ */
protected Pair<List<UserVmVO>, Integer> searchForUserVmsInternal(ListVMsUsageHistoryCmd cmd) { protected Pair<List<UserVmVO>, Integer> searchForUserVmsInternal(ListVMsUsageHistoryCmd cmd) {
final Long id = cmd.getId();
Account caller = CallContext.current().getCallingAccount();
List<Long> permittedAccounts = new ArrayList<>();
boolean recursive = AccountTypesWithRecursiveUsageAccess.contains(caller.getType());
Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(null, recursive, null);
accountMgr.buildACLSearchParameters(caller, id, null, null, permittedAccounts, domainIdRecursiveListProject, true, false);
Long domainId = domainIdRecursiveListProject.first();
Boolean isRecursive = domainIdRecursiveListProject.second();
Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
Filter searchFilter = new Filter(UserVmVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); Filter searchFilter = new Filter(UserVmVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds()); List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
String name = cmd.getName(); String name = cmd.getName();
String keyword = cmd.getKeyword(); String keyword = cmd.getKeyword();
SearchBuilder<UserVmVO> sb = userVmDao.createSearchBuilder(); SearchBuilder<UserVmVO> sb = userVmDao.createSearchBuilder();
sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getId()); // select distinct
accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
sb.and("idIN", sb.entity().getId(), SearchCriteria.Op.IN); sb.and("idIN", sb.entity().getId(), SearchCriteria.Op.IN);
sb.and("displayName", sb.entity().getDisplayName(), SearchCriteria.Op.LIKE); sb.and("displayName", sb.entity().getDisplayName(), SearchCriteria.Op.LIKE);
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
SearchCriteria<UserVmVO> sc = sb.create(); SearchCriteria<UserVmVO> sc = sb.create();
accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
sc.setParameters("idIN", ids.toArray()); sc.setParameters("idIN", ids.toArray());
} }
@ -269,7 +288,14 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements
sc.addAnd("displayName", SearchCriteria.Op.SC, ssc); sc.addAnd("displayName", SearchCriteria.Op.SC, ssc);
} }
return userVmDao.searchAndCount(sc, searchFilter); Pair<List<UserVmVO>, Integer> uniqueVmPair = userVmDao.searchAndCount(sc, searchFilter);
Integer count = uniqueVmPair.second();
if (count == 0) {
return new Pair<>(new ArrayList<>(), count);
}
List<Long> vmIds = uniqueVmPair.first().stream().map(UserVmVO::getId).collect(Collectors.toList());
List<UserVmVO> vms = userVmDao.listByIds(vmIds);
return new Pair<>(vms, count);
} }
/** /**
@ -331,17 +357,49 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements
* @return the list of VMs. * @return the list of VMs.
*/ */
protected Pair<List<VolumeVO>, Integer> searchForVolumesInternal(ListVolumesUsageHistoryCmd cmd) { protected Pair<List<VolumeVO>, Integer> searchForVolumesInternal(ListVolumesUsageHistoryCmd cmd) {
final Long id = cmd.getId();
Account caller = CallContext.current().getCallingAccount();
List<Long> permittedAccounts = new ArrayList<>();
boolean recursive = AccountTypesWithRecursiveUsageAccess.contains(caller.getType());
Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(null, recursive, null);
accountMgr.buildACLSearchParameters(caller, id, null, null, permittedAccounts, domainIdRecursiveListProject, true, false);
Long domainId = domainIdRecursiveListProject.first();
Boolean isRecursive = domainIdRecursiveListProject.second();
Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
Filter searchFilter = new Filter(VolumeVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); Filter searchFilter = new Filter(VolumeVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds()); List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
String name = cmd.getName(); String name = cmd.getName();
String keyword = cmd.getKeyword(); String keyword = cmd.getKeyword();
SearchBuilder<VolumeVO> sb = volumeDao.createSearchBuilder(); SearchBuilder<VolumeVO> sb = volumeDao.createSearchBuilder();
sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getId()); // select distinct
accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
sb.and("idIN", sb.entity().getId(), SearchCriteria.Op.IN); sb.and("idIN", sb.entity().getId(), SearchCriteria.Op.IN);
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
boolean shouldListSystemVmVolumes = accountMgr.isRootAdmin(CallContext.current().getCallingAccountId());
List<Long> vmIds = new ArrayList<>();
if (!shouldListSystemVmVolumes) {
SearchBuilder<UserVmVO> vmSearch = userVmDao.createSearchBuilder();
vmSearch.select(null, SearchCriteria.Func.DISTINCT, vmSearch.entity().getId());
accountMgr.buildACLSearchBuilder(vmSearch, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
SearchCriteria<UserVmVO> vmSc = vmSearch.create();
accountMgr.buildACLSearchCriteria(vmSc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
List<UserVmVO> vms = userVmDao.search(vmSc, null);
vmIds = vms.stream().map(UserVmVO::getId).collect(Collectors.toList());
if (vmIds.isEmpty()) {
sb.and("instanceIdNull", sb.entity().getInstanceId(), SearchCriteria.Op.NULL);
} else {
sb.and().op("instanceIdNull", sb.entity().getInstanceId(), SearchCriteria.Op.NULL);
sb.or("instanceIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN);
sb.cp();
}
}
SearchCriteria<VolumeVO> sc = sb.create(); SearchCriteria<VolumeVO> sc = sb.create();
accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
sc.setParameters("idIN", ids.toArray()); sc.setParameters("idIN", ids.toArray());
} }
@ -354,8 +412,18 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements
ssc.addOr("state", SearchCriteria.Op.EQ, keyword); ssc.addOr("state", SearchCriteria.Op.EQ, keyword);
sc.addAnd("name", SearchCriteria.Op.SC, ssc); sc.addAnd("name", SearchCriteria.Op.SC, ssc);
} }
if (!shouldListSystemVmVolumes && CollectionUtils.isNotEmpty(vmIds)) {
sc.setParameters("instanceIds", vmIds.toArray());
}
return volumeDao.searchAndCount(sc, searchFilter); Pair<List<VolumeVO>, Integer> uniqueVolumePair = volumeDao.searchAndCount(sc, searchFilter);
Integer count = uniqueVolumePair.second();
if (count == 0) {
return new Pair<>(new ArrayList<>(), count);
}
List<Long> volumeIds = uniqueVolumePair.first().stream().map(VolumeVO::getId).collect(Collectors.toList());
List<VolumeVO> volumes = volumeDao.listByIds(volumeIds);
return new Pair<>(volumes, count);
} }
/** /**

View File

@ -19,13 +19,16 @@ package org.apache.cloudstack.metrics;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.cloudstack.api.ListVMsUsageHistoryCmd; import org.apache.cloudstack.api.ListVMsUsageHistoryCmd;
import org.apache.cloudstack.api.ListVolumesUsageHistoryCmd;
import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.response.VmMetricsStatsResponse; import org.apache.cloudstack.response.VmMetricsStatsResponse;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.junit.Assert; import org.junit.Assert;
@ -35,12 +38,18 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.vm.UserVmVO; import com.cloud.vm.UserVmVO;
@ -75,6 +84,12 @@ public class MetricsServiceImplTest {
@Mock @Mock
VmStatsDao vmStatsDaoMock; VmStatsDao vmStatsDaoMock;
@Mock
AccountManager accountManager;
@Mock
VolumeDao volumeDao;
@Captor @Captor
ArgumentCaptor<String> stringCaptor1, stringCaptor2; ArgumentCaptor<String> stringCaptor1, stringCaptor2;
@ -95,12 +110,26 @@ public class MetricsServiceImplTest {
@Mock @Mock
VmStatsVO vmStatsVOMock; VmStatsVO vmStatsVOMock;
@Mock
Account mockAccount;
@Mock
ListVolumesUsageHistoryCmd listVolumesUsageHistoryCmdMock;
@Mock
VolumeVO volumeVOMock;
@Mock
SearchBuilder<VolumeVO> volumeSearchBuilderMock;
@Mock
SearchCriteria<VolumeVO> volumeSearchCriteriaMock;
@Mock
Filter filterMock;
private void prepareSearchCriteriaWhenUseSetParameters() { private void prepareSearchCriteriaWhenUseSetParameters() {
Mockito.doNothing().when(scMock).setParameters(Mockito.anyString(), Mockito.any()); Mockito.doNothing().when(scMock).setParameters(Mockito.anyString(), Mockito.any());
} }
private void preparesearchForUserVmsInternalTest() { private void preparesearchForUserVmsInternalTest() {
Mockito.when(mockAccount.getType()).thenReturn(Account.Type.NORMAL);
expectedVmListAndCounter = new Pair<>(Arrays.asList(userVmVOMock), 1); expectedVmListAndCounter = new Pair<>(Arrays.asList(userVmVOMock), 1);
Mockito.doReturn(1L).when(listVMsUsageHistoryCmdMock).getStartIndex(); Mockito.doReturn(1L).when(listVMsUsageHistoryCmdMock).getStartIndex();
@ -111,8 +140,10 @@ public class MetricsServiceImplTest {
Mockito.doReturn(userVmVOMock).when(sbMock).entity(); Mockito.doReturn(userVmVOMock).when(sbMock).entity();
Mockito.doReturn(scMock).when(sbMock).create(); Mockito.doReturn(scMock).when(sbMock).create();
Mockito.doReturn(new Pair<List<UserVmVO>, Integer>(Arrays.asList(userVmVOMock), 1)) Mockito.doReturn(expectedVmListAndCounter)
.when(userVmDaoMock).searchAndCount(Mockito.any(), Mockito.any()); .when(userVmDaoMock).searchAndCount(Mockito.any(), Mockito.any());
Mockito.doReturn(expectedVmListAndCounter.first())
.when(userVmDaoMock).listByIds(Mockito.anyList());
} }
@Test @Test
@ -124,12 +155,17 @@ public class MetricsServiceImplTest {
Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getName(); Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getName();
Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getKeyword(); Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getKeyword();
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock); try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Mockito.when(callContextMock.getCallingAccount()).thenReturn(mockAccount);
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock);
Mockito.verify(scMock).setParameters(stringCaptor1.capture(), objectArrayCaptor.capture()); Mockito.verify(scMock).setParameters(stringCaptor1.capture(), objectArrayCaptor.capture());
Assert.assertEquals("idIN", stringCaptor1.getValue()); Assert.assertEquals("idIN", stringCaptor1.getValue());
Assert.assertEquals(fakeVmId1, objectArrayCaptor.getAllValues().get(0)[0]); Assert.assertEquals(fakeVmId1, objectArrayCaptor.getAllValues().get(0)[0]);
Assert.assertEquals(expectedVmListAndCounter, result); Assert.assertEquals(expectedVmListAndCounter, result);
}
} }
@Test @Test
@ -141,13 +177,17 @@ public class MetricsServiceImplTest {
Mockito.doReturn(expected).when(listVMsUsageHistoryCmdMock).getIds(); Mockito.doReturn(expected).when(listVMsUsageHistoryCmdMock).getIds();
Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getName(); Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getName();
Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getKeyword(); Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getKeyword();
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Mockito.when(callContextMock.getCallingAccount()).thenReturn(mockAccount);
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock);
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock); Mockito.verify(scMock).setParameters(stringCaptor1.capture(), objectArrayCaptor.capture());
Assert.assertEquals("idIN", stringCaptor1.getValue());
Mockito.verify(scMock).setParameters(stringCaptor1.capture(), objectArrayCaptor.capture()); Assert.assertArrayEquals(expected.toArray(), objectArrayCaptor.getAllValues().get(0));
Assert.assertEquals("idIN", stringCaptor1.getValue()); Assert.assertEquals(expectedVmListAndCounter, result);
Assert.assertArrayEquals(expected.toArray(), objectArrayCaptor.getAllValues().get(0)); }
Assert.assertEquals(expectedVmListAndCounter, result);
} }
@Test @Test
@ -159,12 +199,17 @@ public class MetricsServiceImplTest {
Mockito.doReturn("fakeName").when(listVMsUsageHistoryCmdMock).getName(); Mockito.doReturn("fakeName").when(listVMsUsageHistoryCmdMock).getName();
Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getKeyword(); Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getKeyword();
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock); try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Mockito.when(callContextMock.getCallingAccount()).thenReturn(mockAccount);
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock);
Mockito.verify(scMock).setParameters(stringCaptor1.capture(), objectArrayCaptor.capture()); Mockito.verify(scMock).setParameters(stringCaptor1.capture(), objectArrayCaptor.capture());
Assert.assertEquals("displayName", stringCaptor1.getValue()); Assert.assertEquals("displayName", stringCaptor1.getValue());
Assert.assertEquals("%fakeName%", objectArrayCaptor.getValue()[0]); Assert.assertEquals("%fakeName%", objectArrayCaptor.getValue()[0]);
Assert.assertEquals(expectedVmListAndCounter, result); Assert.assertEquals(expectedVmListAndCounter, result);
}
} }
@Test @Test
@ -177,16 +222,21 @@ public class MetricsServiceImplTest {
Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getName(); Mockito.doReturn(null).when(listVMsUsageHistoryCmdMock).getName();
Mockito.doReturn("fakeKeyword").when(listVMsUsageHistoryCmdMock).getKeyword(); Mockito.doReturn("fakeKeyword").when(listVMsUsageHistoryCmdMock).getKeyword();
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock); try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Mockito.when(callContextMock.getCallingAccount()).thenReturn(mockAccount);
Pair<List<UserVmVO>, Integer> result = spy.searchForUserVmsInternal(listVMsUsageHistoryCmdMock);
Mockito.verify(scMock, Mockito.times(2)).addOr(stringCaptor1.capture(), opCaptor.capture(), objectArrayCaptor.capture()); Mockito.verify(scMock, Mockito.times(2)).addOr(stringCaptor1.capture(), opCaptor.capture(), objectArrayCaptor.capture());
List<String> conditions = stringCaptor1.getAllValues(); List<String> conditions = stringCaptor1.getAllValues();
List<Object[]> params = objectArrayCaptor.getAllValues(); List<Object[]> params = objectArrayCaptor.getAllValues();
Assert.assertEquals("displayName", conditions.get(0)); Assert.assertEquals("displayName", conditions.get(0));
Assert.assertEquals("state", conditions.get(1)); Assert.assertEquals("state", conditions.get(1));
Assert.assertEquals("%fakeKeyword%", params.get(0)[0]); Assert.assertEquals("%fakeKeyword%", params.get(0)[0]);
Assert.assertEquals("fakeKeyword", params.get(1)[0]); Assert.assertEquals("fakeKeyword", params.get(1)[0]);
Assert.assertEquals(expectedVmListAndCounter, result); Assert.assertEquals(expectedVmListAndCounter, result);
}
} }
@Test @Test
@ -317,4 +367,57 @@ public class MetricsServiceImplTest {
spy.createStatsResponse(Arrays.asList(vmStatsVOMock)); spy.createStatsResponse(Arrays.asList(vmStatsVOMock));
} }
@Test
public void searchForVolumesInternalWithValidParameters() {
Mockito.doReturn(null).when(listVolumesUsageHistoryCmdMock).getId();
Mockito.doReturn(Arrays.asList(1L, 2L)).when(listVolumesUsageHistoryCmdMock).getIds();
Mockito.doReturn("volumeName").when(listVolumesUsageHistoryCmdMock).getName();
Mockito.doReturn("keyword").when(listVolumesUsageHistoryCmdMock).getKeyword();
Mockito.doReturn(volumeSearchBuilderMock).when(volumeDao).createSearchBuilder();
Mockito.doReturn(volumeVOMock).when(volumeSearchBuilderMock).entity();
SearchBuilder vmSearchBuilderMock = Mockito.mock(SearchBuilder.class);
Mockito.doReturn(vmSearchBuilderMock).when(userVmDaoMock).createSearchBuilder();
Mockito.doReturn(userVmVOMock).when(vmSearchBuilderMock).entity();
Mockito.doReturn(volumeSearchCriteriaMock).when(volumeSearchBuilderMock).create();
Mockito.doReturn(volumeSearchCriteriaMock).when(volumeDao).createSearchCriteria();
Mockito.doReturn(new Pair<>(Arrays.asList(volumeVOMock), 1)).when(volumeDao).searchAndCount(Mockito.any(), Mockito.any());
Mockito.doReturn(Arrays.asList(volumeVOMock)).when(volumeDao).listByIds(Mockito.anyList());
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Mockito.when(callContextMock.getCallingAccount()).thenReturn(mockAccount);
Pair<List<VolumeVO>, Integer> result = spy.searchForVolumesInternal(listVolumesUsageHistoryCmdMock);
Assert.assertNotNull(result);
Assert.assertEquals(1, result.second().intValue());
Assert.assertEquals(volumeVOMock, result.first().get(0));
}
}
@Test
public void searchForVolumesInternalWithValidParametersNoItem() {
Mockito.doReturn(1L).when(listVolumesUsageHistoryCmdMock).getId();
Mockito.doReturn(volumeSearchBuilderMock).when(volumeDao).createSearchBuilder();
Mockito.doReturn(volumeVOMock).when(volumeSearchBuilderMock).entity();
Mockito.doReturn(volumeSearchCriteriaMock).when(volumeSearchBuilderMock).create();
Mockito.doReturn(new Pair<>(Collections.emptyList(), 0)).when(volumeDao).searchAndCount(Mockito.any(), Mockito.any());
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Mockito.when(callContextMock.getCallingAccount()).thenReturn(mockAccount);
Mockito.when(callContextMock.getCallingAccountId()).thenReturn(1L);
Mockito.when(accountManager.isRootAdmin(1L)).thenReturn(true);
Mockito.when(mockAccount.getType()).thenReturn(Account.Type.ADMIN);
Pair<List<VolumeVO>, Integer> result = spy.searchForVolumesInternal(listVolumesUsageHistoryCmdMock);
Assert.assertNotNull(result);
Assert.assertEquals(0, result.second().intValue());
}
}
} }

View File

@ -448,8 +448,8 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl extends BasePrimaryDataStor
@Override @Override
public boolean cancelMaintain(DataStore store) { public boolean cancelMaintain(DataStore store) {
storagePoolAutmation.cancelMaintain(store);
dataStoreHelper.cancelMaintain(store); dataStoreHelper.cancelMaintain(store);
storagePoolAutmation.cancelMaintain(store);
return true; return true;
} }

View File

@ -158,7 +158,7 @@
<cs.jakarta.xml.bind.version>2.3.3</cs.jakarta.xml.bind.version> <cs.jakarta.xml.bind.version>2.3.3</cs.jakarta.xml.bind.version>
<cs.jaxws.version>2.3.7</cs.jaxws.version> <cs.jaxws.version>2.3.7</cs.jaxws.version>
<cs.jersey-client.version>2.26</cs.jersey-client.version> <cs.jersey-client.version>2.26</cs.jersey-client.version>
<cs.jetty.version>9.4.51.v20230217</cs.jetty.version> <cs.jetty.version>9.4.58.v20250814</cs.jetty.version>
<cs.jetty-maven-plugin.version>9.4.27.v20200227</cs.jetty-maven-plugin.version> <cs.jetty-maven-plugin.version>9.4.27.v20200227</cs.jetty-maven-plugin.version>
<cs.jna.version>5.5.0</cs.jna.version> <cs.jna.version>5.5.0</cs.jna.version>
<cs.joda-time.version>2.12.5</cs.joda-time.version> <cs.joda-time.version>2.12.5</cs.joda-time.version>

View File

@ -68,14 +68,14 @@ class configFileOps:
for entry in self.entries: for entry in self.entries:
if entry.op == "add": if entry.op == "add":
if entry.separator == "=": if entry.separator == "=":
matchString = "^\ *" + entry.name + ".*" matchString = r"^\ *" + entry.name + ".*"
elif entry.separator == " ": elif entry.separator == " ":
matchString = "^\ *" + entry.name + "\ *" + entry.value matchString = r"^\ *" + entry.name + r"\ *" + entry.value
else: else:
if entry.separator == "=": if entry.separator == "=":
matchString = "^\ *" + entry.name + "\ *=\ *" + entry.value matchString = r"^\ *" + entry.name + r"\ *=\ *" + entry.value
else: else:
matchString = "^\ *" + entry.name + "\ *" + entry.value matchString = r"^\ *" + entry.name + r"\ *" + entry.value
match = re.match(matchString, line) match = re.match(matchString, line)
if match is not None: if match is not None:

View File

@ -45,8 +45,11 @@ class networkConfig:
if not cmd.isSuccess(): if not cmd.isSuccess():
logging.debug("Failed to get default route") logging.debug("Failed to get default route")
raise CloudRuntimeException("Failed to get default route") raise CloudRuntimeException("Failed to get default route")
output = cmd.getStdout().strip()
result = cmd.getStdout().split(" ") result = output.split(" ")
if len(result) < 2:
logging.debug("Output for the default route incomplete: %s. It should have been '<GATEWAY> <DEVICE>'" % output)
raise CloudRuntimeException("Output for the default route incomplete")
gateway = result[0] gateway = result[0]
dev = result[1] dev = result[1]
@ -150,10 +153,10 @@ class networkConfig:
if line.find("HWaddr") != -1: if line.find("HWaddr") != -1:
macAddr = line.split("HWaddr ")[1].strip(" ") macAddr = line.split("HWaddr ")[1].strip(" ")
elif line.find("inet ") != -1: elif line.find("inet ") != -1:
m = re.search("addr:(.*)\ *Bcast:(.*)\ *Mask:(.*)", line) m = re.search(r"addr:([^\s]+)\s*Bcast:([^\s]+)\s*Mask:([^\s]+)", line)
if m is not None: if m is not None:
ipAddr = m.group(1).rstrip(" ") ipAddr = m.group(1).strip()
netmask = m.group(3).rstrip(" ") netmask = m.group(3).strip()
if networkConfig.isBridgePort(dev): if networkConfig.isBridgePort(dev):
type = "brport" type = "brport"

View File

@ -63,7 +63,7 @@ class bash:
return self.stdout.decode('utf-8').strip('\n') return self.stdout.decode('utf-8').strip('\n')
def getLines(self): def getLines(self):
return self.stdout.decode('utf-8').strip('\n') return self.stdout.decode('utf-8').strip('\n').split('\n')
def getStderr(self): def getStderr(self):
return self.stderr.decode('utf-8').strip('\n') return self.stderr.decode('utf-8').strip('\n')

View File

@ -5335,7 +5335,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
//Validation - 1.3 //Validation - 1.3
if (resourceIdStr != null) { if (resourceIdStr != null) {
resourceId = resourceManagerUtil.getResourceId(resourceIdStr, resourceType); resourceId = resourceManagerUtil.getResourceId(resourceIdStr, resourceType, true);
} }
List<? extends ResourceDetail> detailList = new ArrayList<>(); List<? extends ResourceDetail> detailList = new ArrayList<>();

View File

@ -483,6 +483,8 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
throw new InvalidParameterValueException("Cannot create Network ACL Item. ACL Id or network Id is required"); throw new InvalidParameterValueException("Cannot create Network ACL Item. ACL Id or network Id is required");
} }
Network network = networkModel.getNetwork(createNetworkACLCmd.getNetworkId()); Network network = networkModel.getNetwork(createNetworkACLCmd.getNetworkId());
Account caller = CallContext.current().getCallingAccount();
_accountMgr.checkAccess(caller, null, true, network);
if (network.getVpcId() == null) { if (network.getVpcId() == null) {
throw new InvalidParameterValueException("Network: " + network.getUuid() + " does not belong to VPC"); throw new InvalidParameterValueException("Network: " + network.getUuid() + " does not belong to VPC");
} }
@ -744,6 +746,7 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ
if (networkId != null) { if (networkId != null) {
final Network network = _networkDao.findById(networkId); final Network network = _networkDao.findById(networkId);
_accountMgr.checkAccess(caller, null, true, network);
aclId = network.getNetworkACLId(); aclId = network.getNetworkACLId();
if (aclId == null) { if (aclId == null) {
// No aclId associated with the network. // No aclId associated with the network.

View File

@ -154,6 +154,7 @@ import com.cloud.org.Cluster;
import com.cloud.org.Grouping; import com.cloud.org.Grouping;
import com.cloud.org.Managed; import com.cloud.org.Managed;
import com.cloud.serializer.GsonHelper; import com.cloud.serializer.GsonHelper;
import com.cloud.server.ManagementService;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.service.dao.ServiceOfferingDetailsDao;
@ -271,6 +272,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
private ServiceOfferingDetailsDao _serviceOfferingDetailsDao; private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
@Inject @Inject
private UserVmManager userVmManager; private UserVmManager userVmManager;
@Inject
ManagementService managementService;
private List<? extends Discoverer> _discoverers; private List<? extends Discoverer> _discoverers;
@ -1936,6 +1939,9 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
@Override @Override
public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException { public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException {
managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE,
Boolean.TRUE.equals(cmd.getIsTagARule()));
return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(), return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(),
cmd.getAllocationState(), cmd.getUrl(), cmd.getHostTags(), cmd.getIsTagARule(), cmd.getAnnotation(), false); cmd.getAllocationState(), cmd.getUrl(), cmd.getHostTags(), cmd.getIsTagARule(), cmd.getAnnotation(), false);
} }

View File

@ -1040,6 +1040,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
protected List<DeploymentPlanner> _planners; protected List<DeploymentPlanner> _planners;
private boolean jsInterpretationEnabled = false;
private final List<HypervisorType> supportedHypervisors = new ArrayList<>(); private final List<HypervisorType> supportedHypervisors = new ArrayList<>();
public List<DeploymentPlanner> getPlanners() { public List<DeploymentPlanner> getPlanners() {
@ -1126,6 +1128,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
supportedHypervisors.add(HypervisorType.KVM); supportedHypervisors.add(HypervisorType.KVM);
supportedHypervisors.add(HypervisorType.XenServer); supportedHypervisors.add(HypervisorType.XenServer);
jsInterpretationEnabled = JsInterpretationEnabled.value();
return true; return true;
} }
@ -4022,8 +4026,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
cmdList.add(ListGuestVlansCmd.class); cmdList.add(ListGuestVlansCmd.class);
cmdList.add(AssignVolumeCmd.class); cmdList.add(AssignVolumeCmd.class);
cmdList.add(ListSecondaryStorageSelectorsCmd.class); cmdList.add(ListSecondaryStorageSelectorsCmd.class);
cmdList.add(CreateSecondaryStorageSelectorCmd.class); if (jsInterpretationEnabled) {
cmdList.add(UpdateSecondaryStorageSelectorCmd.class); cmdList.add(CreateSecondaryStorageSelectorCmd.class);
cmdList.add(UpdateSecondaryStorageSelectorCmd.class);
}
cmdList.add(RemoveSecondaryStorageSelectorCmd.class); cmdList.add(RemoveSecondaryStorageSelectorCmd.class);
cmdList.add(ListAffectedVmsForStorageScopeChangeCmd.class); cmdList.add(ListAffectedVmsForStorageScopeChangeCmd.class);
@ -4066,7 +4072,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Override @Override
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {vmPasswordLength, sshKeyLength, humanReadableSizes, customCsIdentifier}; return new ConfigKey<?>[] {vmPasswordLength, sshKeyLength, humanReadableSizes, customCsIdentifier,
JsInterpretationEnabled};
} }
protected class EventPurgeTask extends ManagedContextRunnable { protected class EventPurgeTask extends ManagedContextRunnable {
@ -5523,4 +5530,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
_lockControllerListener = lockControllerListener; _lockControllerListener = lockControllerListener;
} }
@Override
public void checkJsInterpretationAllowedIfNeededForParameterValue(String paramName, boolean paramValue) {
if (!paramValue || jsInterpretationEnabled) {
return;
}
throw new InvalidParameterValueException(String.format(
"The parameter %s cannot be set to true as JS interpretation is disabled",
paramName));
}
} }

View File

@ -213,6 +213,7 @@ import com.cloud.org.Grouping.AllocationState;
import com.cloud.resource.ResourceState; import com.cloud.resource.ResourceState;
import com.cloud.server.ConfigurationServer; import com.cloud.server.ConfigurationServer;
import com.cloud.server.ManagementServer; import com.cloud.server.ManagementServer;
import com.cloud.server.ManagementService;
import com.cloud.server.StatsCollector; import com.cloud.server.StatsCollector;
import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.ImageFormat;
@ -398,6 +399,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
ConfigurationDao configurationDao; ConfigurationDao configurationDao;
@Inject @Inject
private ImageStoreDetailsUtil imageStoreDetailsUtil; private ImageStoreDetailsUtil imageStoreDetailsUtil;
@Inject
ManagementService managementService;
protected List<StoragePoolDiscoverer> _discoverers; protected List<StoragePoolDiscoverer> _discoverers;
@ -1015,6 +1018,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
throw new PermissionDeniedException(String.format("Cannot perform this operation, Zone is currently disabled: %s", zone)); throw new PermissionDeniedException(String.format("Cannot perform this operation, Zone is currently disabled: %s", zone));
} }
managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE,
Boolean.TRUE.equals(cmd.isTagARule()));
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put("zoneId", zone.getId()); params.put("zoneId", zone.getId());
params.put("clusterId", clusterId); params.put("clusterId", clusterId);
@ -1197,6 +1203,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
// Input validation // Input validation
Long id = cmd.getId(); Long id = cmd.getId();
managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE,
Boolean.TRUE.equals(cmd.isTagARule()));
StoragePoolVO pool = _storagePoolDao.findById(id); StoragePoolVO pool = _storagePoolDao.findById(id);
if (pool == null) { if (pool == null) {
throw new IllegalArgumentException("Unable to find storage pool with ID: " + id); throw new IllegalArgumentException("Unable to find storage pool with ID: " + id);
@ -2688,7 +2697,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
} }
private void updateStoragePoolHostVOAndBytes(StoragePool pool, long hostId, ModifyStoragePoolAnswer mspAnswer) { @Override
public void updateStoragePoolHostVOAndBytes(StoragePool pool, long hostId, ModifyStoragePoolAnswer mspAnswer) {
StoragePoolHostVO poolHost = _storagePoolHostDao.findByPoolHost(pool.getId(), hostId); StoragePoolHostVO poolHost = _storagePoolHostDao.findByPoolHost(pool.getId(), hostId);
if (poolHost == null) { if (poolHost == null) {
poolHost = new StoragePoolHostVO(pool.getId(), hostId, mspAnswer.getPoolInfo().getLocalPath().replaceAll("//", "/")); poolHost = new StoragePoolHostVO(pool.getId(), hostId, mspAnswer.getPoolInfo().getLocalPath().replaceAll("//", "/"));
@ -2698,8 +2708,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
} }
StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId());
poolVO.setUsedBytes(mspAnswer.getPoolInfo().getCapacityBytes() - mspAnswer.getPoolInfo().getAvailableBytes()); if (!Storage.StoragePoolType.StorPool.equals(poolVO.getPoolType())) {
poolVO.setCapacityBytes(mspAnswer.getPoolInfo().getCapacityBytes()); poolVO.setUsedBytes(mspAnswer.getPoolInfo().getCapacityBytes() - mspAnswer.getPoolInfo().getAvailableBytes());
poolVO.setCapacityBytes(mspAnswer.getPoolInfo().getCapacityBytes());
}
_storagePoolDao.update(pool.getId(), poolVO); _storagePoolDao.update(pool.getId(), poolVO);
} }

View File

@ -346,6 +346,7 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("ModifyStoragePool add succeeded"); logger.debug("ModifyStoragePool add succeeded");
} }
storageManager.updateStoragePoolHostVOAndBytes(pool, host.getId(), (ModifyStoragePoolAnswer) answer);
if (pool.getPoolType() == Storage.StoragePoolType.DatastoreCluster) { if (pool.getPoolType() == Storage.StoragePoolType.DatastoreCluster) {
logger.debug("Started synchronising datastore cluster storage pool {} with vCenter", pool); logger.debug("Started synchronising datastore cluster storage pool {} with vCenter", pool);
storageManager.syncDatastoreClusterStoragePool(pool.getId(), ((ModifyStoragePoolAnswer) answer).getDatastoreClusterChildren(), host.getId()); storageManager.syncDatastoreClusterStoragePool(pool.getId(), ((ModifyStoragePoolAnswer) answer).getDatastoreClusterChildren(), host.getId());

View File

@ -22,6 +22,7 @@ import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
@ -128,6 +129,11 @@ public class ResourceManagerUtilImpl implements ResourceManagerUtil {
@Override @Override
public long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType) { public long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType) {
return getResourceId(resourceId, resourceType, false);
}
@Override
public long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType, boolean checkAccess) {
Class<?> clazz = s_typeMap.get(resourceType); Class<?> clazz = s_typeMap.get(resourceType);
Object entity = entityMgr.findByUuid(clazz, resourceId); Object entity = entityMgr.findByUuid(clazz, resourceId);
if (entity != null) { if (entity != null) {
@ -138,6 +144,11 @@ public class ResourceManagerUtilImpl implements ResourceManagerUtil {
} }
entity = entityMgr.findById(clazz, resourceId); entity = entityMgr.findById(clazz, resourceId);
if (entity != null) { if (entity != null) {
if (checkAccess && entity instanceof ControlledEntity) {
ControlledEntity controlledEntity = (ControlledEntity)entity;
Account caller = CallContext.current().getCallingAccount();
accountMgr.checkAccess(caller, null, false, controlledEntity);
}
return ((InternalIdentity)entity).getId(); return ((InternalIdentity)entity).getId();
} }
throw new InvalidParameterValueException("Unable to find resource by id " + resourceId + " and type " + resourceType); throw new InvalidParameterValueException("Unable to find resource by id " + resourceId + " and type " + resourceType);

View File

@ -627,7 +627,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
boolean dataDiskDeletetionResult = true; boolean dataDiskDeletetionResult = true;
List<VMTemplateVO> dataDiskTemplates = templateDao.listByParentTemplatetId(template.getId()); List<VMTemplateVO> dataDiskTemplates = templateDao.listByParentTemplatetId(template.getId());
if (dataDiskTemplates != null && dataDiskTemplates.size() > 0) { if (CollectionUtils.isNotEmpty(dataDiskTemplates)) {
logger.info("Template: {} has Datadisk template(s) associated with it. Delete Datadisk templates before deleting the template", template); logger.info("Template: {} has Datadisk template(s) associated with it. Delete Datadisk templates before deleting the template", template);
for (VMTemplateVO dataDiskTemplate : dataDiskTemplates) { for (VMTemplateVO dataDiskTemplate : dataDiskTemplates) {
logger.info("Delete Datadisk template: {} from image store: {}", dataDiskTemplate, imageStore); logger.info("Delete Datadisk template: {} from image store: {}", dataDiskTemplate, imageStore);
@ -693,6 +693,9 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
if (success) { if (success) {
if ((imageStores != null && imageStores.size() > 1) && (profile.getZoneIdList() != null)) { if ((imageStores != null && imageStores.size() > 1) && (profile.getZoneIdList() != null)) {
//if template is stored in more than one image stores, and the zone id is not null, then don't delete other templates. //if template is stored in more than one image stores, and the zone id is not null, then don't delete other templates.
if (templateMgr.TemplateDeleteFromPrimaryStorage.value()) {
templateMgr.evictTemplateFromStoragePoolsForZones(template.getId(), profile.getZoneIdList());
}
return cleanupTemplate(template, success); return cleanupTemplate(template, success);
} }
@ -705,7 +708,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
// find all eligible image stores for this template // find all eligible image stores for this template
List<DataStore> iStores = templateMgr.getImageStoreByTemplate(template.getId(), null); List<DataStore> iStores = templateMgr.getImageStoreByTemplate(template.getId(), null);
if (iStores == null || iStores.size() == 0) { if (CollectionUtils.isEmpty(iStores)) {
// remove any references from template_zone_ref // remove any references from template_zone_ref
List<VMTemplateZoneVO> templateZones = templateZoneDao.listByTemplateId(template.getId()); List<VMTemplateZoneVO> templateZones = templateZoneDao.listByTemplateId(template.getId());
if (templateZones != null) { if (templateZones != null) {
@ -726,6 +729,10 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
} }
if (templateMgr.TemplateDeleteFromPrimaryStorage.value()) {
templateMgr.evictTemplateFromStoragePoolsForZones(template.getId(), profile.getZoneIdList());
}
// remove its related ACL permission // remove its related ACL permission
Pair<Class<?>, Long> templateClassForId = new Pair<>(VirtualMachineTemplate.class, template.getId()); Pair<Class<?>, Long> templateClassForId = new Pair<>(VirtualMachineTemplate.class, template.getId());
_messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, templateClassForId); _messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, templateClassForId);

View File

@ -1024,33 +1024,53 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
return adapter.delete(new TemplateProfile(userId, template, zoneId)); return adapter.delete(new TemplateProfile(userId, template, zoneId));
} }
private Boolean templateIsUnusedInPool(VMTemplateStoragePoolVO templatePoolVO) {
VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId());
// If this is a routing template, consider it in use
if (template.getTemplateType() == TemplateType.SYSTEM) {
return false;
}
// If the template is not yet downloaded to the pool, consider it in use
if (templatePoolVO.getDownloadState() != Status.DOWNLOADED) {
return false;
}
if (template.getFormat() == ImageFormat.ISO || _volumeDao.isAnyVolumeActivelyUsingTemplateOnPool(template.getId(), templatePoolVO.getPoolId())) {
return false;
}
return true;
}
@Override @Override
public List<VMTemplateStoragePoolVO> getUnusedTemplatesInPool(StoragePoolVO pool) { public List<VMTemplateStoragePoolVO> getUnusedTemplatesInPool(StoragePoolVO pool) {
List<VMTemplateStoragePoolVO> unusedTemplatesInPool = new ArrayList<VMTemplateStoragePoolVO>(); List<VMTemplateStoragePoolVO> unusedTemplatesInPool = new ArrayList<VMTemplateStoragePoolVO>();
List<VMTemplateStoragePoolVO> allTemplatesInPool = _tmpltPoolDao.listByPoolId(pool.getId()); List<VMTemplateStoragePoolVO> allTemplatesInPool = _tmpltPoolDao.listByPoolId(pool.getId());
for (VMTemplateStoragePoolVO templatePoolVO : allTemplatesInPool) { for (VMTemplateStoragePoolVO templatePoolVO : allTemplatesInPool) {
VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId()); if (templateIsUnusedInPool(templatePoolVO)) {
// If this is a routing template, consider it in use
if (template.getTemplateType() == TemplateType.SYSTEM) {
continue;
}
// If the template is not yet downloaded to the pool, consider it in
// use
if (templatePoolVO.getDownloadState() != Status.DOWNLOADED) {
continue;
}
if (template.getFormat() != ImageFormat.ISO && !_volumeDao.isAnyVolumeActivelyUsingTemplateOnPool(template.getId(), pool.getId())) {
unusedTemplatesInPool.add(templatePoolVO); unusedTemplatesInPool.add(templatePoolVO);
} }
} }
return unusedTemplatesInPool; return unusedTemplatesInPool;
} }
@Override
public void evictTemplateFromStoragePoolsForZones(Long templateId, List<Long> zoneIds) {
List<Long> poolIds = new ArrayList<>();
if (CollectionUtils.isNotEmpty(zoneIds)) {
List<StoragePoolVO> pools = _poolDao.listByDataCenterIds(zoneIds);
poolIds = pools.stream().map(StoragePoolVO::getId).collect(Collectors.toList());
}
List<VMTemplateStoragePoolVO> templateStoragePoolVOS = _tmpltPoolDao.listByPoolIdsAndTemplate(poolIds, templateId);
for (VMTemplateStoragePoolVO templateStoragePoolVO: templateStoragePoolVOS) {
if (templateIsUnusedInPool(templateStoragePoolVO)) {
evictTemplateFromStoragePool(templateStoragePoolVO);
}
}
}
@Override @Override
@DB @DB
public void evictTemplateFromStoragePool(VMTemplateStoragePoolVO templatePoolVO) { public void evictTemplateFromStoragePool(VMTemplateStoragePoolVO templatePoolVO) {
@ -2368,7 +2388,10 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
@Override @Override
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {AllowPublicUserTemplates, TemplatePreloaderPoolSize, ValidateUrlIsResolvableBeforeRegisteringTemplate}; return new ConfigKey<?>[] {AllowPublicUserTemplates,
TemplatePreloaderPoolSize,
ValidateUrlIsResolvableBeforeRegisteringTemplate,
TemplateDeleteFromPrimaryStorage};
} }
public List<TemplateAdapter> getTemplateAdapters() { public List<TemplateAdapter> getTemplateAdapters() {

View File

@ -607,10 +607,20 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
} }
protected Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, Long ownerDomainId, Long ownerAccountId, Long zoneId) { protected Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, Long ownerDomainId, Long ownerAccountId, Long zoneId) {
validateNetworkCidrSize(ownerAccountId, cidrSize); validateNetworkCidrSize(cidrSize);
List<DataCenterIpv4GuestSubnetVO> subnets = getZoneSubnetsForAccount(ownerDomainId, ownerAccountId, zoneId); List<DataCenterIpv4GuestSubnetVO> subnets = getZoneSubnetsForAccount(ownerDomainId, ownerAccountId, zoneId);
for (DataCenterIpv4GuestSubnetVO subnet : subnets) { for (DataCenterIpv4GuestSubnetVO subnet : subnets) {
Ipv4GuestSubnetNetworkMap result = getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnet); Ipv4GuestSubnetNetworkMap result = getIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnet);
if (result != null) {
return result;
}
}
Boolean isAutoAllocationEnabled = RoutedIPv4NetworkCidrAutoAllocationEnabled.valueIn(ownerAccountId);
if (!Boolean.TRUE.equals(isAutoAllocationEnabled)) {
throw new InvalidParameterValueException("CIDR auto-allocation is disabled for this account");
}
for (DataCenterIpv4GuestSubnetVO subnet : subnets) {
Ipv4GuestSubnetNetworkMap result = createIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnet);
if (result != null) { if (result != null) {
return result; return result;
} }
@ -618,11 +628,11 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return null; return null;
} }
protected Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, DataCenterIpv4GuestSubnetVO subnet) { protected Ipv4GuestSubnetNetworkMap getIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, DataCenterIpv4GuestSubnetVO subnet) {
Ipv4GuestSubnetNetworkMap map = ipv4GuestSubnetNetworkMapDao.findFirstAvailable(subnet.getId(), cidrSize); return ipv4GuestSubnetNetworkMapDao.findFirstAvailable(subnet.getId(), cidrSize);
if (map != null) { }
return map;
} protected Ipv4GuestSubnetNetworkMap createIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, DataCenterIpv4GuestSubnetVO subnet) {
try { try {
return createIpv4SubnetFromParentSubnet(subnet, cidrSize); return createIpv4SubnetFromParentSubnet(subnet, cidrSize);
} catch (Exception ex) { } catch (Exception ex) {
@ -631,6 +641,14 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return null; return null;
} }
protected Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, DataCenterIpv4GuestSubnetVO subnet) {
Ipv4GuestSubnetNetworkMap map = getIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnet);
if (map != null) {
return map;
}
return createIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnet);
}
protected void getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(String networkCidr, Long ownerDomainId, Long ownerAccountId, Long zoneId) { protected void getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(String networkCidr, Long ownerDomainId, Long ownerAccountId, Long zoneId) {
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(networkCidr); Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(networkCidr);
if (subnetMap != null) { if (subnetMap != null) {
@ -693,13 +711,9 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
} }
} }
private void validateNetworkCidrSize(long accountId, Integer networkCidrSize) { private void validateNetworkCidrSize(Integer networkCidrSize) {
if (networkCidrSize == null) { if (networkCidrSize == null) {
throw new CloudRuntimeException("network/vpc CidrSize is null"); throw new InvalidParameterValueException("network/vpc CidrSize is null");
}
Boolean isAutoAllocationEnabled = RoutedIPv4NetworkCidrAutoAllocationEnabled.valueIn(accountId);
if (!Boolean.TRUE.equals(isAutoAllocationEnabled)) {
throw new CloudRuntimeException("CIDR auto-allocation is disabled for this account");
} }
} }
@ -755,7 +769,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
// Allocate a subnet automatically // Allocate a subnet automatically
String networkCidr = getFreeNetworkCidr(subnetsInFreeIpRanges, networkCidrSize); String networkCidr = getFreeNetworkCidr(subnetsInFreeIpRanges, networkCidrSize);
if (networkCidr == null) { if (networkCidr == null) {
throw new CloudRuntimeException("Failed to automatically allocate a subnet with specified cidrsize"); throw new InvalidParameterValueException("Failed to automatically allocate a subnet with specified cidrsize");
} }
return networkCidr; return networkCidr;
} }

View File

@ -71,7 +71,6 @@ import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuru;
import com.cloud.hypervisor.HypervisorGuruManager; import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.network.IpAddressManager;
import com.cloud.network.Network; import com.cloud.network.Network;
import com.cloud.network.NetworkModel; import com.cloud.network.NetworkModel;
import com.cloud.network.Networks; import com.cloud.network.Networks;
@ -271,8 +270,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
@Inject @Inject
private PhysicalNetworkDao physicalNetworkDao; private PhysicalNetworkDao physicalNetworkDao;
@Inject @Inject
private IpAddressManager ipAddressManager;
@Inject
private StoragePoolHostDao storagePoolHostDao; private StoragePoolHostDao storagePoolHostDao;
@Inject @Inject
private HypervisorGuruManager hypervisorGuruManager; private HypervisorGuruManager hypervisorGuruManager;
@ -2613,10 +2610,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
} }
String macAddress = networkModel.getNextAvailableMacAddressInNetwork(networkId); String macAddress = networkModel.getNextAvailableMacAddressInNetwork(networkId);
String ipAddress = null;
if (network.getGuestType() != Network.GuestType.L2) { String ipAddress = network.getGuestType() != Network.GuestType.L2 ? "auto" : null;
ipAddress = ipAddressManager.acquireGuestIpAddress(network, null);
}
Network.IpAddresses requestedIpPair = new Network.IpAddresses(ipAddress, null, macAddress); Network.IpAddresses requestedIpPair = new Network.IpAddresses(ipAddress, null, macAddress);

View File

@ -545,12 +545,12 @@ public class RoutedIpv4ManagerImplTest {
DataCenterIpv4GuestSubnetVO subnet3 = Mockito.mock(DataCenterIpv4GuestSubnetVO.class); DataCenterIpv4GuestSubnetVO subnet3 = Mockito.mock(DataCenterIpv4GuestSubnetVO.class);
when(dataCenterIpv4GuestSubnetDao.listNonDedicatedByDataCenterId(zoneId)).thenReturn(Arrays.asList(subnet3)); when(dataCenterIpv4GuestSubnetDao.listNonDedicatedByDataCenterId(zoneId)).thenReturn(Arrays.asList(subnet3));
doReturn(null).doReturn(null).doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(eq(cidrSize), any()); doReturn(null).doReturn(null).doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).getIpv4SubnetForGuestNetworkOrVpcInternal(eq(cidrSize), any());
Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, domainId, accountId, zoneId); Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, domainId, accountId, zoneId);
Assert.assertEquals(ipv4GuestSubnetNetworkMap, result); Assert.assertEquals(ipv4GuestSubnetNetworkMap, result);
verify(routedIpv4Manager, times(3)).getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(eq(cidrSize), any()); verify(routedIpv4Manager, times(3)).getIpv4SubnetForGuestNetworkOrVpcInternal(eq(cidrSize), any());
} }
@Test @Test

View File

@ -22,13 +22,15 @@ set -x
function configure_locale() { function configure_locale() {
grep LANG=en_US.UTF-8 /etc/default/locale && \ grep LANG=en_US.UTF-8 /etc/default/locale && \
grep LC_ALL=en_US.UTF-8 /etc/default/locale && \ grep LC_ALL=en_US.UTF-8 /etc/default/locale && \
grep "en_US.UTF-8 UTF-8" /etc/locale.gen && grep "^en_US.UTF-8 UTF-8" /etc/locale.gen &&
return return
cat >> /etc/default/locale << EOF cat >> /etc/default/locale << EOF
LANG=en_US.UTF-8 LANG=en_US.UTF-8
LC_ALL=en_US.UTF-8 LC_ALL=en_US.UTF-8
EOF EOF
grep "^en_US.UTF-8 UTF-8" /etc/locale.gen || \
cat >> /etc/locale.gen << EOF cat >> /etc/locale.gen << EOF
en_US.UTF-8 UTF-8 en_US.UTF-8 UTF-8
EOF EOF

View File

@ -97,5 +97,6 @@
"basicZoneEnabled": true, "basicZoneEnabled": true,
"multipleServer": false, "multipleServer": false,
"allowSettingTheme": true, "allowSettingTheme": true,
"docHelpMappings": {} "docHelpMappings": {},
"notifyLatestCSVersion": true
} }

View File

@ -317,7 +317,6 @@ const user = {
const result = response.listusersresponse.user[0] const result = response.listusersresponse.user[0]
commit('SET_INFO', result) commit('SET_INFO', result)
commit('SET_NAME', result.firstname + ' ' + result.lastname) commit('SET_NAME', result.firstname + ' ' + result.lastname)
store.dispatch('SetCsLatestVersion', result.rolename)
resolve(cachedApis) resolve(cachedApis)
}).catch(error => { }).catch(error => {
reject(error) reject(error)
@ -564,6 +563,9 @@ const user = {
commit('SET_DOMAIN_STORE', domainStore) commit('SET_DOMAIN_STORE', domainStore)
}, },
SetCsLatestVersion ({ commit }, rolename) { SetCsLatestVersion ({ commit }, rolename) {
if (!vueProps.$config.notifyLatestCSVersion) {
return
}
const lastFetchTs = store.getters.latestVersion?.fetchedTs ? store.getters.latestVersion.fetchedTs : 0 const lastFetchTs = store.getters.latestVersion?.fetchedTs ? store.getters.latestVersion.fetchedTs : 0
if (rolename === 'Root Admin' && (+new Date() - lastFetchTs) > 24 * 60 * 60 * 1000) { if (rolename === 'Root Admin' && (+new Date() - lastFetchTs) > 24 * 60 * 60 * 1000) {
axios.get( axios.get(

View File

@ -396,7 +396,7 @@ export default {
placeHolder: 'message.error.server', placeHolder: 'message.error.server',
required: true, required: true,
display: { display: {
primaryStorageProtocol: ['nfs', 'iscsi', 'gluster', 'SMB', 'Linstor'] primaryStorageProtocol: ['nfs', 'iscsi', 'gluster', 'SMB', 'Linstor', 'datastorecluster', 'vmfs']
} }
}, },
{ {

View File

@ -1510,10 +1510,10 @@ export default {
} }
path += '/' + this.prefillContent.primaryStorageVmfsDatastore path += '/' + this.prefillContent.primaryStorageVmfsDatastore
if (protocol === 'vmfs') { if (protocol === 'vmfs') {
url = this.vmfsURL('dummy', path) url = this.vmfsURL(server, path)
} }
if (protocol === 'datastorecluster') { if (protocol === 'datastorecluster') {
url = this.datastoreclusterURL('dummy', path) url = this.datastoreclusterURL(server, path)
} }
} else if (protocol === 'iscsi') { } else if (protocol === 'iscsi') {
let iqn = this.prefillContent?.primaryStorageTargetIQN || '' let iqn = this.prefillContent?.primaryStorageTargetIQN || ''

View File

@ -413,7 +413,7 @@ export default {
for (const index in net.traffics) { for (const index in net.traffics) {
if (this.hypervisor === 'VMware') { if (this.hypervisor === 'VMware') {
delete this.physicalNetworks[idx].traffics[index].label delete this.physicalNetworks[idx].traffics[index].label
} else { } else if (!net.traffics[index].label) {
this.physicalNetworks[idx].traffics[index].label = '' this.physicalNetworks[idx].traffics[index].label = ''
} }
const traffic = net.traffics[index] const traffic = net.traffics[index]

View File

@ -29,6 +29,7 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -157,13 +158,7 @@ public class Script implements Callable<String> {
boolean obscureParam = false; boolean obscureParam = false;
for (int i = 0; i < command.length; i++) { for (int i = 0; i < command.length; i++) {
String cmd = command[i]; String cmd = command[i];
if (StringUtils.isNotEmpty(cmd) && cmd.startsWith("vi://")) { if (sanitizeViCmdParameter(cmd, builder) || sanitizeRbdFileFormatCmdParameter(cmd, builder)) {
String[] tokens = cmd.split("@");
if (tokens.length >= 2) {
builder.append("vi://").append("******@").append(tokens[1]).append(" ");
} else {
builder.append("vi://").append("******").append(" ");
}
continue; continue;
} }
if (obscureParam) { if (obscureParam) {
@ -181,6 +176,41 @@ public class Script implements Callable<String> {
return builder.toString(); return builder.toString();
} }
private boolean sanitizeViCmdParameter(String cmd, StringBuilder builder) {
if (StringUtils.isEmpty(cmd) || !cmd.startsWith("vi://")) {
return false;
}
String[] tokens = cmd.split("@");
if (tokens.length >= 2) {
builder.append("vi://").append("******@").append(tokens[1]).append(" ");
} else {
builder.append("vi://").append("******").append(" ");
}
return true;
}
private boolean sanitizeRbdFileFormatCmdParameter(String cmd, StringBuilder builder) {
if (StringUtils.isEmpty(cmd) || !cmd.startsWith("rbd:") || !cmd.contains("key=")) {
return false;
}
String[] tokens = cmd.split("key=");
if (tokens.length != 2) {
return false;
}
String tokenWithKey = tokens[1];
String[] options = tokenWithKey.split(":");
if (options.length > 1) {
String optionsAfterKey = String.join(":", Arrays.copyOfRange(options, 1, options.length));
builder.append(tokens[0]).append("key=").append("******").append(":").append(optionsAfterKey).append(" ");
} else {
builder.append(tokens[0]).append("key=").append("******").append(" ");
}
return true;
}
public long getTimeout() { public long getTimeout() {
return _timeout; return _timeout;
} }

View File

@ -19,31 +19,52 @@ package org.apache.cloudstack.utils.jsinterpreter;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.apache.commons.collections.MapUtils; import javax.script.Bindings;
import org.apache.logging.log4j.Logger; import javax.script.Compilable;
import org.apache.logging.log4j.LogManager; import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;
import com.cloud.utils.exception.CloudRuntimeException; import org.apache.commons.collections.MapUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openjdk.nashorn.api.scripting.ClassFilter;
import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory; import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
import javax.script.ScriptEngine; import com.cloud.utils.exception.CloudRuntimeException;
/** /**
* A class to execute JavaScript scripts, with the possibility to inject context to the scripts. * Executes JavaScript with strong restrictions to mitigate RCE risks.
* - Disables Java interop via --no-java AND a deny-all ClassFilter
* - Disables Nashorn syntax extensions
* - Uses Bindings instead of string-splicing variables
* - Fresh ScriptContext per execution, with timeout on a daemon worker
*/ */
public class JsInterpreter implements Closeable { public class JsInterpreter implements Closeable {
protected Logger logger = LogManager.getLogger(JsInterpreter.class); protected Logger logger = LogManager.getLogger(JsInterpreter.class);
protected static final List<String> RESTRICTED_TOKENS = Arrays.asList( "engine", "context", "factory",
"Java", "java", "Packages"," javax", "load", "loadWithNewGlobal", "print", "factory", "getClass",
"runCommand", "Runtime", "exec", "ProcessBuilder", "Thread", "thread", "Threads", "Class", "class");
protected ScriptEngine interpreter; protected ScriptEngine interpreter;
protected String interpreterName; protected String interpreterName;
private final String injectingLogMessage = "Injecting variable [%s] with value [%s] into the JS interpreter."; private final String injectingLogMessage = "Injecting variable [%s] with value [%s] into the JS interpreter.";
@ -51,21 +72,40 @@ public class JsInterpreter implements Closeable {
private TimeUnit defaultTimeUnit = TimeUnit.MILLISECONDS; private TimeUnit defaultTimeUnit = TimeUnit.MILLISECONDS;
private long timeout; private long timeout;
private String timeoutDefaultMessage; private String timeoutDefaultMessage;
protected Map<String, String> variables = new LinkedHashMap<>();
// Store variables as Objects; they go into Bindings (no code splicing)
protected Map<String, Object> variables = new LinkedHashMap<>();
/** Deny-all filter: no Java class is visible from scripts. */
static final class DenyAllClassFilter implements ClassFilter {
@Override public boolean exposeToScripts(String className) { return false; }
}
/** /**
* Constructor created exclusively for unit testing. * Constructor created exclusively for unit testing.
*/ */
protected JsInterpreter() { protected JsInterpreter() { }
}
public JsInterpreter(long timeout) { public JsInterpreter(long timeout) {
this.timeout = timeout; this.timeout = timeout;
this.timeoutDefaultMessage = String.format("Timeout (in milliseconds) defined in the global setting [quota.activationrule.timeout]: [%s].", this.timeout); this.timeoutDefaultMessage = String.format(
"Timeout (in milliseconds) defined in the global setting [quota.activationrule.timeout]: [%s].", this.timeout);
if (System.getProperty("nashorn.args") == null) {
System.setProperty("nashorn.args", "--no-java --no-syntax-extensions");
}
this.executor = new ThreadPoolExecutor(
1, 1, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
r -> {
Thread t = new Thread(r, "JsInterpreter-worker");
t.setDaemon(true);
return t;
}
);
executor = Executors.newSingleThreadExecutor();
NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
this.interpreterName = factory.getEngineName(); this.interpreterName = factory.getEngineName();
logger.trace(String.format("Initiating JS interpreter: %s.", interpreterName)); logger.trace(String.format("Initiating JS interpreter: %s.", interpreterName));
@ -73,49 +113,53 @@ public class JsInterpreter implements Closeable {
} }
protected void setScriptEngineDisablingJavaLanguage(NashornScriptEngineFactory factory) { protected void setScriptEngineDisablingJavaLanguage(NashornScriptEngineFactory factory) {
interpreter = factory.getScriptEngine("--no-java"); String[] opts = new String[] {
"--no-java",
"--no-syntax-extensions",
};
interpreter = factory.getScriptEngine(
opts,
JsInterpreter.class.getClassLoader(),
new DenyAllClassFilter()
);
} }
/** /** Discards the current variables map and create a new one. */
* Discards the current variables map and create a new one.
*/
public void discardCurrentVariables() { public void discardCurrentVariables() {
logger.trace("Discarding current variables map and creating a new one."); logger.trace("Discarding current variables map and creating a new one.");
variables = new LinkedHashMap<>(); variables = new LinkedHashMap<>();
} }
/** /**
* Adds the parameters to a Map that will be converted to JS variables right before executing the script. * Adds a variable that will be exposed via ENGINE_SCOPE bindings.
* @param key The name of the variable. * Safe against code injection (no string concatenation).
* @param value The value of the variable.
*/ */
public void injectVariable(String key, String value) { public void injectVariable(String key, Object value) {
logger.trace(String.format(injectingLogMessage, key, value)); if (key == null) return;
logger.trace(String.format(injectingLogMessage, key, String.valueOf(value)));
variables.put(key, value); variables.put(key, value);
} }
/** /**
* Adds the parameter, surrounded by double quotes, to a Map that will be converted to a JS variable right before executing the script. * @deprecated Not needed when using Bindings; kept for source compatibility.
* @param key The name of the variable. * Prefer {@link #injectVariable(String, Object)}.
* @param value The value of the variable.
*/ */
@Deprecated
public void injectStringVariable(String key, String value) { public void injectStringVariable(String key, String value) {
if (value == null) { if (value == null) {
logger.trace(String.format("Not injecting [%s] because its value is null.", key)); logger.trace(String.format("Not injecting [%s] because its value is null.", key));
return; return;
} }
value = String.format("\"%s\"", value); injectVariable(key, value);
logger.trace(String.format(injectingLogMessage, key, value));
variables.put(key, value);
} }
/** /**
* Injects the variables to the script and execute it. * Injects the variables via Bindings and executes the script with a fresh context.
* @param script Code to be executed. * @param script Code to be executed.
* @return The result of the executed script. * @return The result of the executed script.
*/ */
public Object executeScript(String script) { public Object executeScript(String script) {
script = addVariablesToScript(script); Objects.requireNonNull(script, "script");
logger.debug(String.format("Executing script [%s].", script)); logger.debug(String.format("Executing script [%s].", script));
@ -126,43 +170,60 @@ public class JsInterpreter implements Closeable {
} }
protected Object executeScriptInThread(String script) { protected Object executeScriptInThread(String script) {
Callable<Object> task = () -> interpreter.eval(script); final Callable<Object> task = () -> {
final SimpleScriptContext ctx = new SimpleScriptContext();
Future<Object> future = executor.submit(task); final Bindings engineBindings = new SimpleBindings();
if (MapUtils.isNotEmpty(variables)) {
engineBindings.putAll(variables);
}
for (String token : RESTRICTED_TOKENS) {
engineBindings.put(token, null);
}
ctx.setBindings(engineBindings, ScriptContext.ENGINE_SCOPE);
final StringWriter out = new StringWriter();
ctx.setWriter(out);
try {
final CompiledScript compiled = ((Compilable) interpreter).compile(script);
Object result = compiled.eval(ctx);
if (out.getBuffer().length() > 0) {
logger.info("Script produced output on stdout: [{}]", out);
}
return result;
} catch (ScriptException se) {
String msg = se.getMessage() == null ? "Script error" : se.getMessage();
throw new ScriptException("Script error: " + msg, se.getFileName(), se.getLineNumber(), se.getColumnNumber());
}
};
final Future<Object> future = executor.submit(task);
try { try {
return future.get(this.timeout, defaultTimeUnit); return future.get(this.timeout, defaultTimeUnit);
} catch (TimeoutException | InterruptedException | ExecutionException e) { } catch (TimeoutException e) {
String message = String.format("Unable to execute script [%s] due to [%s]", script, e.getMessage()); String message = String.format(
"Execution of script [%s] took too long and timed out. %s", script, timeoutDefaultMessage);
if (e instanceof TimeoutException) {
message = String.format("Execution of script [%s] took too long and timed out. %s", script, timeoutDefaultMessage);
}
logger.error(message, e); logger.error(message, e);
throw new CloudRuntimeException(message, e); throw new CloudRuntimeException(message, e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
String message = String.format("Execution of script [%s] was interrupted.", script);
logger.error(message, e);
throw new CloudRuntimeException(message, e);
} catch (ExecutionException e) {
Throwable cause = e.getCause() == null ? e : e.getCause();
String message = String.format("Unable to execute script [%s] due to [%s]", script, cause.getMessage());
logger.error(message, cause);
throw new CloudRuntimeException(message, cause);
} finally { } finally {
future.cancel(true); future.cancel(true);
} }
} }
protected String addVariablesToScript(String script) {
if (MapUtils.isEmpty(variables)) {
logger.trace(String.format("There is no variables to add to script [%s]. Returning.", script));
return script;
}
String variablesToString = "";
for (Map.Entry<String, String> variable : variables.entrySet()) {
variablesToString = String.format("%s %s = %s;", variablesToString, variable.getKey(), variable.getValue());
}
return String.format("%s %s", variablesToString, script);
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
executor.shutdown(); executor.shutdownNow();
} }
} }

View File

@ -16,12 +16,12 @@
//under the License. //under the License.
package org.apache.cloudstack.utils.jsinterpreter; package org.apache.cloudstack.utils.jsinterpreter;
import com.cloud.utils.exception.CloudRuntimeException; import java.io.IOException;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.IOException; import com.cloud.utils.exception.CloudRuntimeException;
public class TagAsRuleHelper { public class TagAsRuleHelper {
@ -32,7 +32,6 @@ public class TagAsRuleHelper {
public static boolean interpretTagAsRule(String rule, String tags, long timeout) { public static boolean interpretTagAsRule(String rule, String tags, long timeout) {
String script = PARSE_TAGS + rule; String script = PARSE_TAGS + rule;
tags = String.format("'%s'", StringEscapeUtils.escapeEcmaScript(tags));
try (JsInterpreter jsInterpreter = new JsInterpreter(timeout)) { try (JsInterpreter jsInterpreter = new JsInterpreter(timeout)) {
jsInterpreter.injectVariable("tags", tags); jsInterpreter.injectVariable("tags", tags);
Object scriptReturn = jsInterpreter.executeScript(script); Object scriptReturn = jsInterpreter.executeScript(script);

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.utils.jsinterpreter;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -27,7 +28,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import com.cloud.utils.exception.CloudRuntimeException; import javax.script.ScriptEngine;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -36,9 +38,10 @@ import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.openjdk.nashorn.api.scripting.ClassFilter;
import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory; import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
import javax.script.ScriptEngine; import com.cloud.utils.exception.CloudRuntimeException;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class JsInterpreterTest { public class JsInterpreterTest {
@ -61,30 +64,6 @@ public class JsInterpreterTest {
Assert.assertTrue(executor.isShutdown()); Assert.assertTrue(executor.isShutdown());
} }
@Test
public void addVariablesToScriptTestVariablesMapIsEmptyReturnScript() {
String script = "a + b";
jsInterpreterSpy.variables = new LinkedHashMap<>();
String result = jsInterpreterSpy.addVariablesToScript(script);
Assert.assertEquals(script, result);
}
@Test
public void addVariablesToScriptTestVariablesMapIsNotEmptyInjectVariableToScript() {
String script = "a + b";
String var1 = "{test: \"test\"}";
jsInterpreterSpy.injectVariable("var1", var1);
jsInterpreterSpy.injectVariable("var2", var1);
String expected = String.format(" var1 = %s; var2 = %s; %s", var1, var1, script);
String result = jsInterpreterSpy.addVariablesToScript(script);
Assert.assertEquals(expected, result);
}
@Test @Test
public void executeScriptTestReturnResultOfScriptExecution() { public void executeScriptTestReturnResultOfScriptExecution() {
String script = "5"; String script = "5";
@ -154,7 +133,7 @@ public class JsInterpreterTest {
@Test @Test
public void discardCurrentVariablesTestInstantiateNewMap() { public void discardCurrentVariablesTestInstantiateNewMap() {
Map<String, String> variables = new LinkedHashMap<>(); Map<String, Object> variables = new LinkedHashMap<>();
variables.put("a", "b"); variables.put("a", "b");
variables.put("b", null); variables.put("b", null);
@ -170,12 +149,14 @@ public class JsInterpreterTest {
NashornScriptEngineFactory nashornScriptEngineFactoryMock = Mockito.spy(NashornScriptEngineFactory.class); NashornScriptEngineFactory nashornScriptEngineFactoryMock = Mockito.spy(NashornScriptEngineFactory.class);
ScriptEngine scriptEngineMock = Mockito.mock(ScriptEngine.class); ScriptEngine scriptEngineMock = Mockito.mock(ScriptEngine.class);
Mockito.doReturn(scriptEngineMock).when(nashornScriptEngineFactoryMock).getScriptEngine(Mockito.anyString()); Mockito.doReturn(scriptEngineMock).when(nashornScriptEngineFactoryMock).getScriptEngine(Mockito.any(),
Mockito.any(ClassLoader.class), Mockito.any(ClassFilter.class));
jsInterpreterSpy.setScriptEngineDisablingJavaLanguage(nashornScriptEngineFactoryMock); jsInterpreterSpy.setScriptEngineDisablingJavaLanguage(nashornScriptEngineFactoryMock);
Assert.assertEquals(scriptEngineMock, jsInterpreterSpy.interpreter); Assert.assertEquals(scriptEngineMock, jsInterpreterSpy.interpreter);
Mockito.verify(nashornScriptEngineFactoryMock).getScriptEngine("--no-java"); Mockito.verify(nashornScriptEngineFactoryMock).getScriptEngine(Mockito.any(),
Mockito.any(ClassLoader.class), Mockito.any(ClassFilter.class));
} }
@Test @Test
@ -193,6 +174,46 @@ public class JsInterpreterTest {
jsInterpreterSpy.injectStringVariable("a", "b"); jsInterpreterSpy.injectStringVariable("a", "b");
Assert.assertEquals(jsInterpreterSpy.variables.get("a"), "\"b\""); Assert.assertEquals(jsInterpreterSpy.variables.get("a"), "b");
}
@Test
public void executeScriptTestValidScriptShouldPassWithMixedVariables() {
try (JsInterpreter jsInterpreter = new JsInterpreter(1000)) {
jsInterpreter.injectVariable("x", 10);
jsInterpreter.injectVariable("y", "hello");
jsInterpreter.injectVariable("z", true);
String validScript = "var result = x + (z ? 1 : 0); y + '-' + result;";
Object result = jsInterpreter.executeScript(validScript);
Assert.assertEquals("hello-11", result);
} catch (IOException exception) {
Assert.fail("IOException not expected here");
}
}
private void runMaliciousScriptFileTest(String script, String filename) {
try (JsInterpreter jsInterpreter = new JsInterpreter(1000)) {
jsInterpreter.executeScript(script);
} catch (CloudRuntimeException ex) {
Assert.assertTrue(ex.getMessage().contains("Unable to execute script"));
java.io.File f = new java.io.File(filename);
Assert.assertFalse(f.exists());
} catch (IOException exception) {
Assert.fail("IOException not expected here");
}
}
@Test
public void executeScriptTestMaliciousScriptShouldThrowException1() {
String filename = "/tmp/hack1-" + UUID.randomUUID();
String maliciousScript = "Java.type('java.lang.Runtime').getRuntime().exec('touch " + filename + "')";
runMaliciousScriptFileTest(maliciousScript, filename);
}
@Test
public void executeScriptTestMaliciousScriptShouldThrowException2() {
String filename = "/tmp/hack2-" + UUID.randomUUID();
String maliciousScript = "var e=this.engine.getFactory().getScriptEngine('-Dnashorn.args=--no-java=False'); e.eval(\"java.lang.Runtime.getRuntime().exec(['/bin/bash','-c','touch " + filename + "']);\");";
runMaliciousScriptFileTest(maliciousScript, filename);
} }
} }