package io.cattle.platform.iaas.api.volume;
import static io.cattle.platform.core.constants.CommonStatesConstants.*;
import static io.cattle.platform.core.constants.VolumeConstants.*;
import io.cattle.platform.core.constants.VolumeConstants;
import io.cattle.platform.core.model.Backup;
import io.cattle.platform.core.model.Snapshot;
import io.cattle.platform.core.model.Volume;
import io.cattle.platform.iaas.api.filter.common.AbstractDefaultResourceManagerFilter;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.util.type.CollectionUtils;
import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException;
import io.github.ibuildthecloud.gdapi.request.ApiRequest;
import io.github.ibuildthecloud.gdapi.request.resource.ResourceManager;
import io.github.ibuildthecloud.gdapi.util.ResponseCodes;
import io.github.ibuildthecloud.gdapi.validation.ValidationErrorCodes;
import java.util.Map;
import javax.inject.Inject;
public class VolumeRevertRestoreValidationFilter extends AbstractDefaultResourceManagerFilter {
@Inject
ObjectManager objectManager;
@Override
public Class<?>[] getTypeClasses() {
return new Class<?>[] { Volume.class };
}
@Override
public Object resourceAction(String type, ApiRequest request, ResourceManager next) {
if (!request.getAction().equalsIgnoreCase(ACTION_REVERT) && !request.getAction().equalsIgnoreCase(ACTION_RESTORE)) {
return super.resourceAction(type, request, next);
}
Long volumeId = Long.valueOf(request.getId());
Map<String, Object> data = CollectionUtils.toMap(request.getRequestObject());
Long snapshotId = null;
String field = null;
String resourceToCheck = null;
String stateToCheck = null;
if (request.getAction().equalsIgnoreCase(ACTION_REVERT)) {
field = "snapshotId";
snapshotId = (Long)data.get(field);
resourceToCheck = "Snapshot";
Snapshot snapshot = objectManager.loadResource(Snapshot.class, snapshotId);
stateToCheck = snapshot.getState();
// Ensure the snapshot was made from the volume this action is being performed on
if (!snapshot.getVolumeId().equals(volumeId)) {
throw new ClientVisibleException(ResponseCodes.UNPROCESSABLE_ENTITY, ValidationErrorCodes.INVALID_REFERENCE,
"Snapshot is not for the specified volume.", null);
}
} else if (request.getAction().equalsIgnoreCase(ACTION_RESTORE)) {
field = "backupId";
Long backupId = (Long)data.get(field);
Backup backup = objectManager.loadResource(Backup.class, backupId);
snapshotId = backup.getSnapshotId();
stateToCheck = backup.getState();
resourceToCheck = "Backup";
// If the volume we're restoring isn't the same as the volume from which we created the backup, then make sure neither is a root volume.
if (!backup.getVolumeId().equals(volumeId)) {
Volume targetVolume = objectManager.loadResource(Volume.class, volumeId);
Volume sourceVolume = objectManager.loadResource(Volume.class, backup.getVolumeId());
if (isRootVolume(targetVolume) || isRootVolume(sourceVolume)) {
throw new ClientVisibleException(ResponseCodes.UNPROCESSABLE_ENTITY, ValidationErrorCodes.INVALID_REFERENCE,
"When restoring a root volume, the backup must have been created from the same volume.", null);
}
}
}
// Ensure the backup or snapshot is in the created state
if (!CREATED.equalsIgnoreCase(stateToCheck)) {
throw new ClientVisibleException(ResponseCodes.UNPROCESSABLE_ENTITY, ValidationErrorCodes.INVALID_REFERENCE, resourceToCheck
+ " must be in created state.", null);
}
return super.resourceAction(type, request, next);
}
private boolean isRootVolume(Volume volume) {
// While there is no "is a vm's root volume" flag on a volume, for all practical purposes, if the volume has a base-image
// driver-opt, it is a vm's root volume and cannot be restored.
Map<String, Object> opts = DataAccessor.fieldMap(volume, VolumeConstants.FIELD_VOLUME_DRIVER_OPTS);
return opts.containsKey(VolumeConstants.DRIVER_OPT_BASE_IMAGE);
}
}