package org.ovirt.engine.api.restapi.resource;
import static org.ovirt.engine.api.restapi.resource.BackendDataCenterResource.getStoragePools;
import java.net.URI;
import java.util.List;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.ovirt.engine.api.model.Action;
import org.ovirt.engine.api.model.BaseResource;
import org.ovirt.engine.api.model.CreationStatus;
import org.ovirt.engine.api.resource.ActionResource;
import org.ovirt.engine.api.restapi.invocation.Current;
import org.ovirt.engine.api.restapi.invocation.CurrentManager;
import org.ovirt.engine.api.restapi.util.LinkHelper;
import org.ovirt.engine.api.utils.LinkCreator;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.BusinessEntity;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatic;
import org.ovirt.engine.core.common.businessentities.StoragePool;
import org.ovirt.engine.core.common.queries.NameQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.compat.Guid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractBackendActionableResource <R extends BaseResource, Q /* extends IVdcQueryable */ >
extends AbstractBackendSubResource<R, Q> {
private static final Logger log = LoggerFactory.getLogger(AbstractBackendActionableResource.class);
public AbstractBackendActionableResource(String id, Class<R> modelType, Class<Q> entityType) {
super(id, modelType, entityType);
}
protected Response doAction(final VdcActionType task, final VdcActionParametersBase params, final Action action, AbstractBackendResource.PollingType pollingType, EntityResolver entityResolver) {
awaitGrace(action);
try {
VdcReturnValueBase actionResult = doAction(task, params);
if (actionResult.getHasAsyncTasks()) {
if (expectBlocking(action)) {
CreationStatus status = awaitCompletion(actionResult, pollingType);
if (status != CreationStatus.FAILED){
Object model = resolveCreated(actionResult, entityResolver, null);
return actionStatus(status, action, model);
}
return actionStatus(status, action, addLinks(newModel(id)));
} else {
return actionAsync(actionResult, action);
}
} else {
Object model = resolveCreated(actionResult, entityResolver, null);
return actionSuccess(action, model);
}
} catch (Exception e) {
return handleError(e, action);
}
}
protected Object resolveCreated(VdcReturnValueBase result, EntityResolver entityResolver, Class<? extends BaseResource> suggestedParentType) {
try {
return entityResolver.resolve(result.getActionReturnValue());
} catch (Exception e) {
// we tolerate a failure in the entity resolution
// as the substantive action (entity creation) has
// already succeeded
return null;
}
}
protected Response doAction(final VdcActionType task, final VdcActionParametersBase params, final Action action, AbstractBackendResource.PollingType pollingType) {
awaitGrace(action);
try {
VdcReturnValueBase actionResult = doAction(task, params);
if (actionResult.getJobId() != null) {
setJobLink(action, actionResult);
}
if (actionResult.getHasAsyncTasks()) {
if (expectBlocking(action)) {
CreationStatus status = awaitCompletion(actionResult, pollingType);
return actionStatus(status, action, addLinks(newModel(id)));
} else {
return actionAsync(actionResult, action);
}
} else {
return actionSuccess(action, addLinks(newModel(id)));
}
} catch (Exception e) {
return handleError(e, action);
}
}
/**
* Perform an action, managing asynchrony and returning an appropriate
* response with entity returned by backend in action body.
*
* @param task the backend task
* @param params the task parameters
* @param action action representation
* @param entityResolver backend response resolver
*/
protected Response doAction(final VdcActionType task, final VdcActionParametersBase params, final Action action, EntityResolver entityResolver) {
return doAction(task, params, action, PollingType.VDSM_TASKS, entityResolver);
}
/**
* Perform an action, managing asynchrony and returning an appropriate
* response.
*
* @param task the backend task
* @param params the task parameters
* @param action action representation
*/
protected Response doAction(final VdcActionType task, final VdcActionParametersBase params, final Action action) {
return doAction(task, params, action, PollingType.VDSM_TASKS);
}
protected void awaitGrace(Action action) {
if (action.isSetGracePeriod() && action.getGracePeriod().isSetExpiry()) {
delay(action.getGracePeriod().getExpiry());
}
}
protected boolean expectBlocking(Action action) {
return action.isSetAsync() && !action.isAsync();
}
public ActionResource getActionResource(String action, String oid) {
// redirect back to the target resource if action no longer cached
// if not getActionResource() not overridden in resource sub-class
// (in which case async actions are not supported, and the action
// resource should never be queried)
//
return new ActionResource() {
@Override
public Response get() {
URI uri = URI.create(LinkHelper.addLinks(newModel(id)).getHref());
Response.Status status = Response.Status.MOVED_PERMANENTLY;
return Response.status(status).location(uri).build();
}
@Override
public Action getAction() {
return null;
}
};
}
protected Guid getHostId(Action action) {
return getHostId(action.getHost());
}
protected Response handleError(Exception e, Action action) {
try {
return handleError(e, false);
} catch (WebFaultException wfe) {
action.setFault(wfe.getFault());
return actionFailure(action, wfe);
} catch (WebApplicationException wae) {
return actionFailure(action, wae);
}
}
protected Response actionFailure(Action action, WebApplicationException wae) {
action.setStatus(CreationStatus.FAILED.value());
return Response.fromResponse(wae.getResponse()).entity(action).build();
}
protected Response actionSuccess(Action action) {
action.setStatus(CreationStatus.COMPLETE.value());
return Response.ok().entity(action).build();
}
private Response actionSuccess(Action action, Object result) {
setActionItem(action, result);
action.setStatus(CreationStatus.COMPLETE.value());
return Response.ok().entity(action).build();
}
protected Response actionAsync(VdcReturnValueBase actionResult, Action action) {
action.setAsync(true);
String ids = asString(actionResult.getVdsmTaskIdList());
action.setId(ids);
Current current = CurrentManager.get();
String path = current.getRelativePath();
action.setHref(path + "/" + ids);
addOrUpdateLink(action, "parent", path.substring(0, path.lastIndexOf("/")));
addOrUpdateLink(action, "replay", path);
action.setStatus(getAsynchronousStatus(actionResult).value());
return Response.status(ACCEPTED_STATUS).entity(action).build();
}
private String getPath(UriInfo uriInfo) {
return LinkCreator.combine(uriInfo.getBaseUri().getPath(), uriInfo.getPath());
}
protected Guid getStorageDomainId(Action action) {
if (action.getStorageDomain().isSetId()) {
return asGuid(action.getStorageDomain().getId());
} else {
return lookupStorageDomainIdByName(action.getStorageDomain().getName());
}
}
protected Guid getClusterId(Action action) {
if (action.getCluster().isSetId()) {
return asGuid(action.getCluster().getId());
} else {
Cluster cluster = lookupClusterByName(action.getCluster().getName());
return cluster != null ? cluster.getId() : null;
}
}
protected Cluster lookupClusterByName(String name) {
return getEntity(Cluster.class, VdcQueryType.GetClusterByName, new NameQueryParameters(name), "Cluster: name=" + name);
}
protected Guid lookupStorageDomainIdByName(String name) {
if (!isFiltered()) {
StorageDomainStatic storageDomain =
getEntity(org.ovirt.engine.core.common.businessentities.StorageDomainStatic.class,
VdcQueryType.GetStorageDomainByName,
new NameQueryParameters(name),
"Storage: name=" + name);
if (storageDomain != null) {
return storageDomain.getId();
}
return null;
}
else {
List<org.ovirt.engine.core.common.businessentities.StorageDomain> storageDomains =
getBackendCollection(org.ovirt.engine.core.common.businessentities.StorageDomain.class,
VdcQueryType.GetAllStorageDomains,
new VdcQueryParametersBase());
for (org.ovirt.engine.core.common.businessentities.StorageDomain storageDomain : storageDomains) {
if (storageDomain.getStorageName().equals(name)) {
return storageDomain.getId();
}
}
return null;
}
}
public Guid getDataCenterId(Guid storageDomainId) {
List<StoragePool> storagepools = getStoragePools(storageDomainId, this);
return storagepools.size() > 0 ? storagepools.get(0).getId() : null;
}
/**
* This class can be used as a resolver that binds business logic objects to API models by ID.
* The from and to classes are determined by parameters passed to the constructor.
* The returned model object will contain only the ID field of the object.
* */
protected class SimpleIdResolver extends EntityResolver {
private Class<? extends BaseResource> apiClass;
private Class<? extends BusinessEntity<?>> blClass;
private VdcQueryType query;
private Class<? extends VdcQueryParametersBase> queryParamsClass;
/**
* Create a resolver.
*
* @param apiClass Model class whose instance will be returned from lookupEntity.
* @param blClass Business logic class whose ID will be passed to lookupEntity.
* @param query Same as in EntityResolver's constructor.
* @param queryParamsClass Same as in EntityResolver's constructor.
* */
public SimpleIdResolver(
Class<? extends BaseResource> apiClass,
Class<? extends BusinessEntity<?>> blClass,
VdcQueryType query,
Class<? extends VdcQueryParametersBase> queryParamsClass
) {
this.apiClass = apiClass;
this.blClass = blClass;
this.query = query;
this.queryParamsClass = queryParamsClass;
}
@Override
public Object lookupEntity(Object id) throws BackendFailureException {
BusinessEntity<?> blEntity = doGetEntity(blClass,
query, getQueryParams(queryParamsClass, id), id.toString());
try {
BaseResource apiObject = apiClass.getConstructor().newInstance();
apiObject.setId(blEntity.getId().toString());
return LinkHelper.addLinks(apiObject);
} catch (ReflectiveOperationException e) {
// Shouldn't happen, all models have public no-args constructor.
log.error("Failed to instantiate BackendResource", e);
return null;
}
}
}
}