Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ public String toString() {
}

public static ApiCommandResourceType fromString(String value) {
if (StringUtils.isNotEmpty(value) && EnumUtils.isValidEnum(ApiCommandResourceType.class, value)) {
return valueOf(value);
if (StringUtils.isNotBlank(value) && EnumUtils.isValidEnumIgnoreCase(ApiCommandResourceType.class, value)) {
return EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, value);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Date;

import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListAccountResourcesCmd;
import org.apache.cloudstack.api.Parameter;
Expand All @@ -40,6 +41,12 @@ public class ListAsyncJobsCmd extends BaseListAccountResourcesCmd {
@Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "The id of the management server", since="4.19")
private Long managementServerId;

@Parameter(name = ApiConstants.RESOURCE_ID, validations = {ApiArgValidator.UuidString}, type = CommandType.STRING, description = "the ID of the resource associated with the job", since="4.22.1")
private String resourceId;

@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the job", since="4.22.1")
private String resourceType;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand All @@ -52,6 +59,14 @@ public Long getManagementServerId() {
return managementServerId;
}

public String getResourceId() {
return resourceId;
}

public String getResourceType() {
return resourceType;
}

/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
// under the License.
package org.apache.cloudstack.api.command.user.job;


import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
Expand All @@ -34,9 +34,15 @@ public class QueryAsyncJobResultCmd extends BaseCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////

@Parameter(name = ApiConstants.JOB_ID, type = CommandType.UUID, entityType = AsyncJobResponse.class, required = true, description = "The ID of the asynchronous job")
@Parameter(name = ApiConstants.JOB_ID, type = CommandType.UUID, entityType = AsyncJobResponse.class, description = "The ID of the asynchronous job")
private Long id;

@Parameter(name = ApiConstants.RESOURCE_ID, validations = {ApiArgValidator.UuidString}, type = CommandType.STRING, description = "the ID of the resource associated with the job", since="4.22.1")
private String resourceId;

@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the job", since="4.22.1")
private String resourceType;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand All @@ -45,6 +51,14 @@ public Long getId() {
return id;
}

public String getResourceId() {
return resourceId;
}

public String getResourceType() {
return resourceType;
}

/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,30 @@

import com.cloud.utils.db.GenericDao;

import javax.annotation.Nullable;

public interface AsyncJobDao extends GenericDao<AsyncJobVO, Long> {

AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId);

List<AsyncJobVO> findInstancePendingAsyncJobs(String instanceType, Long accountId);

/**
* Finds async job matching the given parameters.
* Non-null parameters are added to search criteria.
* Returns the most recent job by creation date.
* <p>
* When searching by resourceId and resourceType, only one active job
* is expected per resource, so returning a single result is sufficient.
*
* @param id job ID
* @param resourceId resource ID (instanceId)
* @param resourceType resource type (instanceType)
* @return matching job or null
*/
@Nullable
AsyncJobVO findJob(Long id, Long resourceId, String resourceType);

AsyncJobVO findPseudoJob(long threadId, long msid);

void cleanupPseduoJobs(long msid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.util.List;

import org.apache.cloudstack.api.ApiConstants;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.apache.cloudstack.jobs.JobInfo;
Expand All @@ -45,6 +47,7 @@ public class AsyncJobDaoImpl extends GenericDaoBase<AsyncJobVO, Long> implements
private final SearchBuilder<AsyncJobVO> expiringUnfinishedAsyncJobSearch;
private final SearchBuilder<AsyncJobVO> expiringCompletedAsyncJobSearch;
private final SearchBuilder<AsyncJobVO> failureMsidAsyncJobSearch;
private final SearchBuilder<AsyncJobVO> byIdResourceIdResourceTypeSearch;
private final GenericSearchBuilder<AsyncJobVO, Long> asyncJobTypeSearch;
private final GenericSearchBuilder<AsyncJobVO, Long> pendingNonPseudoAsyncJobsSearch;

Expand Down Expand Up @@ -95,6 +98,12 @@ public AsyncJobDaoImpl() {
failureMsidAsyncJobSearch.and("job_cmd", failureMsidAsyncJobSearch.entity().getCmd(), Op.IN);
failureMsidAsyncJobSearch.done();

byIdResourceIdResourceTypeSearch = createSearchBuilder();
byIdResourceIdResourceTypeSearch.and("id", byIdResourceIdResourceTypeSearch.entity().getId(), SearchCriteria.Op.EQ);
byIdResourceIdResourceTypeSearch.and("instanceId", byIdResourceIdResourceTypeSearch.entity().getInstanceId(), SearchCriteria.Op.EQ);
byIdResourceIdResourceTypeSearch.and("instanceType", byIdResourceIdResourceTypeSearch.entity().getInstanceType(), SearchCriteria.Op.EQ);
byIdResourceIdResourceTypeSearch.done();

asyncJobTypeSearch = createSearchBuilder(Long.class);
asyncJobTypeSearch.select(null, SearchCriteria.Func.COUNT, asyncJobTypeSearch.entity().getId());
asyncJobTypeSearch.and("job_info", asyncJobTypeSearch.entity().getCmdInfo(),Op.LIKE);
Expand Down Expand Up @@ -140,6 +149,30 @@ public List<AsyncJobVO> findInstancePendingAsyncJobs(String instanceType, Long a
return listBy(sc);
}

@Override
public AsyncJobVO findJob(Long id, Long resourceId, String resourceType) {
SearchCriteria<AsyncJobVO> sc = byIdResourceIdResourceTypeSearch.create();

if (id == null && resourceId == null && StringUtils.isBlank(resourceType)) {
logger.debug("findJob called with all null parameters");
return null;
}

if (id != null) {
sc.setParameters("id", id);
}
if (resourceId != null && StringUtils.isNotBlank(resourceType)) {
sc.setParameters("instanceType", resourceType);
sc.setParameters("instanceId", resourceId);
}
Filter filter = new Filter(AsyncJobVO.class, "created", false, 0L, 1L);
List<AsyncJobVO> result = searchIncludingRemoved(sc, filter, Boolean.FALSE, false);
if (CollectionUtils.isNotEmpty(result)) {
return result.get(0);
}
return null;
}

@Override
public AsyncJobVO findPseudoJob(long threadId, long msid) {
SearchCriteria<AsyncJobVO> sc = pseudoJobSearch.create();
Expand Down
36 changes: 31 additions & 5 deletions server/src/main/java/com/cloud/api/ApiResponseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import javax.inject.Inject;

import com.cloud.api.query.ResourceIdSupport;
import com.cloud.bgp.ASNumber;
import com.cloud.bgp.ASNumberRange;
import com.cloud.configuration.ConfigurationService;
Expand All @@ -57,6 +59,7 @@
import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiConstants.DomainDetails;
import org.apache.cloudstack.api.ApiConstants.HostDetails;
Expand Down Expand Up @@ -219,6 +222,7 @@
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.framework.jobs.AsyncJob;
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
import org.apache.cloudstack.gui.theme.GuiThemeJoin;
import org.apache.cloudstack.management.ManagementServerHost;
import org.apache.cloudstack.network.BgpPeerVO;
Expand Down Expand Up @@ -447,7 +451,7 @@

import sun.security.x509.X509CertImpl;

public class ApiResponseHelper implements ResponseGenerator {
public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {

protected Logger logger = LogManager.getLogger(ApiResponseHelper.class);
private static final DecimalFormat s_percentFormat = new DecimalFormat("##.##");
Expand Down Expand Up @@ -529,6 +533,8 @@ public class ApiResponseHelper implements ResponseGenerator {
RoutedIpv4Manager routedIpv4Manager;
@Inject
ResourceIconManager resourceIconManager;
@Inject
AsyncJobDao asyncJobDao;

public static String getPrettyDomainPath(String path) {
if (path == null) {
Expand Down Expand Up @@ -2304,16 +2310,26 @@ public TemplatePermissionsResponse createTemplatePermissionsResponse(ResponseVie

@Override
public AsyncJobResponse queryJobResult(final QueryAsyncJobResultCmd cmd) {
final Account caller = CallContext.current().getCallingAccount();
ApiCommandResourceType resourceType = getResourceType(cmd.getResourceType());
String resourceTypeName = Optional.ofNullable(resourceType).map(ApiCommandResourceType::name).orElse(null);

Long resourceId = getResourceId(resourceType, cmd.getResourceId());
Long jobId = cmd.getId();
if (jobId == null && resourceId == null) {
throw new InvalidParameterValueException("Expected parameter job id or parameters resource type and resource id");
}

final AsyncJob job = _entityMgr.findByIdIncludingRemoved(AsyncJob.class, cmd.getId());
final AsyncJob job = asyncJobDao.findJob(jobId, resourceId, resourceTypeName);
if (job == null) {
throw new InvalidParameterValueException("Unable to find a job by id " + cmd.getId());
throw new InvalidParameterValueException("Unable to find a job by id " + jobId + " resource type "
+ cmd.getResourceType() + " resource id " + cmd.getResourceId());
}
jobId = job.getId();

final User userJobOwner = _accountMgr.getUserIncludingRemoved(job.getUserId());
final Account jobOwner = _accountMgr.getAccount(userJobOwner.getAccountId());

final Account caller = CallContext.current().getCallingAccount();
//check permissions
if (_accountMgr.isNormalUser(caller.getId())) {
//regular users can see only jobs they own
Expand All @@ -2324,7 +2340,7 @@ public AsyncJobResponse queryJobResult(final QueryAsyncJobResultCmd cmd) {
_accountMgr.checkAccess(caller, null, true, jobOwner);
}

return createAsyncJobResponse(_jobMgr.queryJob(cmd.getId(), true));
return createAsyncJobResponse(_jobMgr.queryJob(jobId, true));
}

public AsyncJobResponse createAsyncJobResponse(AsyncJob job) {
Expand Down Expand Up @@ -5686,4 +5702,14 @@ public ConsoleSessionResponse createConsoleSessionResponse(ConsoleSession consol
consoleSessionResponse.setObjectName("consolesession");
return consoleSessionResponse;
}

@Override
public EntityManager getEntityManager() {
return _entityMgr;
}

@Override
public AccountManager getAccountManager() {
return _accountMgr;
}
}
45 changes: 28 additions & 17 deletions server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -369,7 +368,7 @@
import com.cloud.vm.dao.VMInstanceDetailsDao;

@Component
public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable {
public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable, ResourceIdSupport {


private static final String ID_FIELD = "id";
Expand Down Expand Up @@ -868,26 +867,14 @@ private Pair<List<Long>, Integer> searchForEventIdsAndCount(ListEventsCmd cmd) {
Integer entryTime = cmd.getEntryTime();
Integer duration = cmd.getDuration();
Long startId = cmd.getStartId();
final String resourceUuid = cmd.getResourceId();
final String resourceTypeStr = cmd.getResourceType();
final String resourceUuid = getResourceUuid(cmd.getResourceId());
final ApiCommandResourceType resourceType = getResourceType(cmd.getResourceType());
final String stateStr = cmd.getState();
ApiCommandResourceType resourceType = null;
Long resourceId = null;
if (resourceTypeStr != null) {
resourceType = ApiCommandResourceType.fromString(resourceTypeStr);
if (resourceType == null) {
throw new InvalidParameterValueException(String.format("Invalid %s", ApiConstants.RESOURCE_TYPE));
}
}
if (resourceUuid != null) {
if (resourceTypeStr == null) {
if (resourceType == null) {
throw new InvalidParameterValueException(String.format("%s parameter must be used with %s parameter", ApiConstants.RESOURCE_ID, ApiConstants.RESOURCE_TYPE));
}
try {
UUID.fromString(resourceUuid);
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException(String.format("Invalid %s", ApiConstants.RESOURCE_ID));
}
Object object = entityManager.findByUuidIncludingRemoved(resourceType.getAssociatedClass(), resourceUuid);
if (object instanceof InternalIdentity) {
resourceId = ((InternalIdentity)object).getId();
Expand Down Expand Up @@ -3204,6 +3191,20 @@ private Pair<List<AsyncJobJoinVO>, Integer> searchForAsyncJobsInternal(ListAsync
sc.setParameters("executingMsid", msHost.getMsid());
}

if (cmd.getResourceType() != null) {
ApiCommandResourceType resourceType = getResourceType(cmd.getResourceType());
sc.addAnd("instanceType", SearchCriteria.Op.EQ, resourceType.toString());

final String resourceId = getResourceUuid(cmd.getResourceId());
if (resourceId == null) {
throw new InvalidParameterValueException("Invalid resource id for the resource type " + resourceType);
}

sc.addAnd("instanceUuid", SearchCriteria.Op.EQ, resourceId);
} else if (cmd.getResourceId() != null) {
throw new InvalidParameterValueException("Resource type must be specified for the resource id");
}

return _jobJoinDao.searchAndCount(sc, searchFilter);
}

Expand Down Expand Up @@ -6260,4 +6261,14 @@ public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {AllowUserViewDestroyedVM, UserVMDeniedDetails, UserVMReadOnlyDetails, SortKeyAscending,
AllowUserViewAllDomainAccounts, AllowUserViewAllDataCenters, SharePublicTemplatesWithOtherDomains, ReturnVmStatsOnVmList};
}

@Override
public EntityManager getEntityManager() {
return entityManager;
}

@Override
public AccountManager getAccountManager() {
return accountMgr;
}
}
Loading
Loading