package org.zstack.simulator.storage.primary.nfs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.zstack.core.thread.AsyncThread;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.rest.RESTFacade;
import org.zstack.kvm.KVMAgentCommands.AgentResponse;
import org.zstack.simulator.AsyncRESTReplyer;
import org.zstack.simulator.kvm.VolumeSnapshotKvmSimulator;
import org.zstack.storage.primary.nfs.NfsPrimaryStorageKVMBackend;
import org.zstack.storage.primary.nfs.NfsPrimaryStorageKVMBackendCommands.*;
import org.zstack.storage.primary.nfs.NfsPrimaryToSftpBackupKVMBackend;
import org.zstack.utils.Utils;
import org.zstack.utils.gson.JSONObjectUtil;
import org.zstack.utils.logging.CLogger;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@Controller
public class NfsPrimaryStorageSimulator {
CLogger logger = Utils.getLogger(NfsPrimaryStorageSimulator.class);
@Autowired
private RESTFacade restf;
@Autowired
private NfsPrimaryStorageSimulatorConfig config;
@Autowired
private VolumeSnapshotKvmSimulator volumeSnapshotKvmSimulator;
private AsyncRESTReplyer replyer = new AsyncRESTReplyer();
class Capacity {
long total;
long avail;
}
private void reply(HttpEntity<String> entity, NfsPrimaryStorageAgentResponse rsp) {
replyer.reply(entity, rsp);
}
private Map<String, Capacity> capacityMap = new HashMap<String, Capacity>();
private void setCapacity(NfsPrimaryStorageAgentCommand cmd, NfsPrimaryStorageAgentResponse rsp) {
rsp.setTotalCapacity(config.totalCapacity);
rsp.setAvailableCapacity(config.availableCapacity);
}
@AsyncThread
private void doGetCapacity(HttpEntity<String> entity) {
GetCapacityCmd cmd = JSONObjectUtil.toObject(entity.getBody(), GetCapacityCmd.class);
GetCapacityResponse rsp = new GetCapacityResponse();
setCapacity(cmd, rsp);
reply(entity, rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.GET_VOLUME_BASE_IMAGE_PATH, method=RequestMethod.POST)
private @ResponseBody String getVolumeBaseImagePath(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
GetVolumeBaseImagePathCmd cmd = JSONObjectUtil.toObject(entity.getBody(), GetVolumeBaseImagePathCmd.class);
GetVolumeBaseImagePathRsp rsp = new GetVolumeBaseImagePathRsp();
rsp.path = config.getVolumeBaseImagePaths.get(cmd.volumeUUid);
reply(entity, rsp);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.UNMOUNT_PRIMARY_STORAGE_PATH, method=RequestMethod.POST)
public @ResponseBody String nfsUnmount(@RequestBody String body) {
if (config.unmountException) {
throw new CloudRuntimeException("unmount exception on purpose");
}
UnmountCmd cmd = JSONObjectUtil.toObject(body, UnmountCmd.class);
AgentResponse rsp = new AgentResponse();
if (config.unmountSuccess) {
rsp.setSuccess(true);
config.unmountCmds.add(cmd);
logger.debug(String.format("Unmount %s", cmd.getMountPath()));
} else {
rsp.setSuccess(false);
rsp.setError("Fail umount on purpose");
}
return JSONObjectUtil.toJsonString(rsp);
}
@AsyncThread
private void doCreateRootVolumeFromTemplate(HttpEntity<String> entity) throws InterruptedException {
CreateRootVolumeFromTemplateCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CreateRootVolumeFromTemplateCmd.class);
CreateRootVolumeFromTemplateResponse rsp = new CreateRootVolumeFromTemplateResponse();
if (!config.createRootVolumeFromTemplateSuccess) {
rsp.setSuccess(false);
rsp.setError("Fail create on purpose");
} else {
logger.debug(String.format("Created root volume from cached template %s to %s", cmd.getTemplatePathInCache(), cmd.getInstallUrl()));
}
reply(entity, rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.MOUNT_PRIMARY_STORAGE_PATH, method=RequestMethod.POST)
public @ResponseBody String nfsMount(@RequestBody String body) {
if (config.mountException) {
throw new CloudRuntimeException("mount exception on purpose");
}
MountCmd cmd = JSONObjectUtil.toObject(body, MountCmd.class);
MountAgentResponse rsp = new MountAgentResponse();
if (config.mountSuccess) {
Capacity cap = new Capacity();
cap.total = config.totalCapacity;
cap.avail = config.availableCapacity;
capacityMap.put(cmd.getUuid(), cap);
rsp.setTotalCapacity(cap.total);
rsp.setAvailableCapacity(cap.avail);
config.mountCmds.add(cmd);
logger.debug(String.format("mount %s to %s", cmd.getUrl(), cmd.getMountPath()));
} else {
rsp.setSuccess(false);
rsp.setError("Fail mount on purpose");
}
return JSONObjectUtil.toJsonString(rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.SYNC_GET_CAPACITY_PATH, method=RequestMethod.POST)
private @ResponseBody String syncGetCapacity(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
GetCapacityCmd cmd = JSONObjectUtil.toObject(entity.getBody(), GetCapacityCmd.class);
GetCapacityResponse rsp = new GetCapacityResponse();
setCapacity(cmd, rsp);
return JSONObjectUtil.toJsonString(rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.GET_CAPACITY_PATH, method=RequestMethod.POST)
private @ResponseBody String getCapacity(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
doGetCapacity(entity);
return null;
}
@RequestMapping(value=NfsPrimaryToSftpBackupKVMBackend.CREATE_VOLUME_FROM_TEMPLATE_PATH, method=RequestMethod.POST)
private @ResponseBody String createRootVolumeFromTemplate(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
if (config.createRootVolumeFromTemplateException) {
throw new CloudRuntimeException("Fail download on purpose");
} else {
doCreateRootVolumeFromTemplate(entity);
}
return null;
}
@RequestMapping(value=NfsPrimaryToSftpBackupKVMBackend.DOWNLOAD_FROM_SFTP_PATH, method=RequestMethod.POST)
private @ResponseBody String downloadFromSftp(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
downloadFromSftp(entity);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.PING_PATH, method=RequestMethod.POST)
private @ResponseBody String ping(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
NfsPrimaryStorageAgentResponse rsp = new NfsPrimaryStorageAgentResponse();
if (!config.pingSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
config.pingCmds.add(JSONObjectUtil.toObject(entity.getBody(), PingCmd.class));
}
reply(entity, rsp);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.DELETE_PATH, method=RequestMethod.POST)
private @ResponseBody String delete(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
DeleteCmd cmd = JSONObjectUtil.toObject(entity.getBody(), DeleteCmd.class);
DeleteResponse rsp = new DeleteResponse();
if (!config.deleteSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
// we don't know if this is deleting snapshot
// volumeSnapshotKvmSimulator will ignore the call if it's not
volumeSnapshotKvmSimulator.delete(cmd.getInstallPath());
config.deleteCmds.add(cmd);
}
reply(entity, rsp);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.MOVE_BITS_PATH, method=RequestMethod.POST)
private @ResponseBody String move(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
move(entity);
return null;
}
@AsyncThread
private void move(HttpEntity<String> entity) {
MoveBitsCmd cmd = JSONObjectUtil.toObject(entity.getBody(), MoveBitsCmd.class);
MoveBitsRsp rsp = new MoveBitsRsp();
if (!config.moveBitsSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
config.moveBitsCmds.add(cmd);
}
reply(entity, rsp);
}
@AsyncThread
private void downloadFromSftp(HttpEntity<String> entity) {
DownloadBitsFromSftpBackupStorageCmd cmd = JSONObjectUtil.toObject(entity.getBody(), DownloadBitsFromSftpBackupStorageCmd.class);
DownloadBitsFromSftpBackupStorageResponse rsp = new DownloadBitsFromSftpBackupStorageResponse();
if (!config.downloadFromSftpSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
logger.debug(entity.getBody());
config.downloadFromSftpCmds.add(cmd);
config.imageCache.add(cmd.getPrimaryStorageInstallPath());
}
reply(entity, rsp);
}
@RequestMapping(value=NfsPrimaryToSftpBackupKVMBackend.UPLOAD_TO_SFTP_PATH, method=RequestMethod.POST)
private @ResponseBody String copyToSftp(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
copyToSftp(entity);
return null;
}
@AsyncThread
private void copyToSftp(HttpEntity<String> entity) {
UploadToSftpCmd cmd = JSONObjectUtil.toObject(entity.getBody(), UploadToSftpCmd.class);
UploadToSftpResponse rsp = new UploadToSftpResponse();
if (!config.uploadToSftp || cmd.getPrimaryStorageInstallPath().equals(config.backupSnapshotFailurePrimaryStorageInstallPath)) {
rsp.setError("no purpose");
rsp.setSuccess(false);
} else {
config.uploadToSftpCmds.add(cmd);
}
reply(entity, rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.OFFLINE_SNAPSHOT_MERGE, method=RequestMethod.POST)
private @ResponseBody String offlineMergeSnapshot(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
OfflineMergeSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), OfflineMergeSnapshotCmd.class);
OfflineMergeSnapshotRsp rsp = new OfflineMergeSnapshotRsp();
if (!config.offlineMergeSnapshotSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
volumeSnapshotKvmSimulator.merge(cmd.getSrcPath(), cmd.getDestPath(), cmd.isFullRebase());
config.offlineMergeSnapshotCmds.add(cmd);
}
reply(entity, rsp);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.CHECK_BITS_PATH, method=RequestMethod.POST)
private @ResponseBody String checkBits(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
checkBits(entity);
return null;
}
@AsyncThread
private void checkBits(HttpEntity<String> entity) {
CheckIsBitsExistingCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CheckIsBitsExistingCmd.class);
CheckIsBitsExistingRsp rsp = new CheckIsBitsExistingRsp();
if (config.checkImageSuccess) {
rsp.setExisting(config.imageCache.contains(cmd.getInstallPath()));
} else {
rsp.setError("on purpose");
rsp.setSuccess(false);
}
reply(entity, rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.CREATE_EMPTY_VOLUME_PATH, method=RequestMethod.POST)
private @ResponseBody String createEmptyVolume(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
doCreateEmptyVolume(entity);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.CREATE_TEMPLATE_FROM_VOLUME_PATH, method=RequestMethod.POST)
private @ResponseBody String createTemplateFromRootVolume(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
doCreateTemplateFromRootVolume(entity);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.REVERT_VOLUME_FROM_SNAPSHOT_PATH, method=RequestMethod.POST)
private @ResponseBody String revertVolumeFromSnapshot(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
RevertVolumeFromSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), RevertVolumeFromSnapshotCmd.class);
RevertVolumeFromSnapshotResponse rsp = new RevertVolumeFromSnapshotResponse();
if (config.revertVolumeFromSnapshotSuccess) {
config.revertVolumeFromSnapshotCmds.add(cmd);
rsp = volumeSnapshotKvmSimulator.revert(cmd);
} else {
rsp.setError("on purpose");
rsp.setSuccess(false);
}
reply(entity, rsp);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.REBASE_MERGE_SNAPSHOT_PATH, method=RequestMethod.POST)
private @ResponseBody String rebaseAndMergeSnapshot(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
rebaseAndMergeSnapshot(entity);
return null;
}
@AsyncThread
private void rebaseAndMergeSnapshot(HttpEntity<String> entity) {
RebaseAndMergeSnapshotsCmd cmd = JSONObjectUtil.toObject(entity.getBody(), RebaseAndMergeSnapshotsCmd.class);
RebaseAndMergeSnapshotsResponse rsp = new RebaseAndMergeSnapshotsResponse();
if (!config.rebaseAndMergeSnapshotSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
Long size = config.rebaseAndMergeSnapshotsCmdSize.get(cmd.getVolumeUuid());
rsp.setSize(size == null ? 0 : size);
Long aszie = config.rebaseAndMergeSnapshotsCmdActualSize.get(cmd.getVolumeUuid());
rsp.setActualSize(aszie == null ? 0 : aszie);
config.rebaseAndMergeSnapshotsCmds.add(cmd);
}
reply(entity, rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.GET_VOLUME_SIZE_PATH, method=RequestMethod.POST)
private @ResponseBody String getVolumeSize(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
GetVolumeActualSizeCmd cmd = JSONObjectUtil.toObject(entity.getBody(), GetVolumeActualSizeCmd.class);
config.getVolumeSizeCmds.add(cmd);
GetVolumeActualSizeRsp rsp = new GetVolumeActualSizeRsp();
Long asize = config.getVolumeSizeCmdActualSize.get(cmd.volumeUuid);
rsp.actualSize = asize == null ? 0 : asize;
Long size = config.getVolumeSizeCmdSize.get(cmd.volumeUuid);
rsp.size = size == null ? 0 : size;
reply(entity, rsp);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.MERGE_SNAPSHOT_PATH, method=RequestMethod.POST)
private @ResponseBody String mergeSnapshot(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
mergeSnapshot(entity);
return null;
}
@AsyncThread
private void mergeSnapshot(HttpEntity<String> entity) {
MergeSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), MergeSnapshotCmd.class);
MergeSnapshotResponse rsp = new MergeSnapshotResponse();
if (!config.mergeSnapshotSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
Long size = config.mergeSnapshotCmdSize.get(cmd.getVolumeUuid());
rsp.setSize(size == null ? 0 : size);
Long asize = config.mergeSnapshotCmdActualSize.get(cmd.getVolumeUuid());
rsp.setActualSize(asize == null ? 0 : asize);
config.mergeSnapshotCmds.add(cmd);
}
reply(entity, rsp);
}
@AsyncThread
private void doCreateTemplateFromRootVolume(HttpEntity<String> entity) {
CreateTemplateFromVolumeCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CreateTemplateFromVolumeCmd.class);
CreateTemplateFromVolumeRsp rsp = new CreateTemplateFromVolumeRsp();
if (!config.createTemplateFromRootVolumeSuccess) {
rsp.setSuccess(false);
rsp.setError("Fail create template on purpose");
} else {
logger.debug(String.format("successfully create template from volume, %s", entity.getBody()));
}
reply(entity, rsp);
}
@AsyncThread
private void doCreateEmptyVolume(HttpEntity<String> entity) {
CreateEmptyVolumeCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CreateEmptyVolumeCmd.class);
CreateEmptyVolumeResponse rsp = new CreateEmptyVolumeResponse();
if (!config.createEmptyVolumeSuccess) {
rsp.setError("failed on purpose");
rsp.setSuccess(false);
} else {
logger.debug(String.format("create empty volume[uuid:%s, mountPath:%s, size:%s]", cmd.getUuid(), cmd.getInstallUrl(), cmd.getSize()));
}
reply(entity, rsp);
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.REMOUNT_PATH, method=RequestMethod.POST)
private @ResponseBody String remount(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
RemountCmd cmd = JSONObjectUtil.toObject(entity.getBody(), RemountCmd.class);
NfsPrimaryStorageAgentResponse rsp = new NfsPrimaryStorageAgentResponse();
if (!config.remountSuccess) {
rsp.setError("on purpose");
rsp.setSuccess(false);
} else {
rsp.setTotalCapacity(config.totalCapacity);
rsp.setAvailableCapacity(config.availableCapacity);
config.remountCmds.add(cmd);
}
reply(entity, rsp);
return null;
}
@RequestMapping(value=NfsPrimaryStorageKVMBackend.UPDATE_MOUNT_POINT_PATH, method=RequestMethod.POST)
private @ResponseBody String updateMountPoint(HttpServletRequest req) throws InterruptedException {
HttpEntity<String> entity = restf.httpServletRequestToHttpEntity(req);
UpdateMountPointCmd cmd = JSONObjectUtil.toObject(entity.getBody(), UpdateMountPointCmd.class);
UpdateMountPointRsp rsp = new UpdateMountPointRsp();
config.updateMountPointCmds.add(cmd);
rsp.setTotalCapacity(config.totalCapacity);
rsp.setAvailableCapacity(config.availableCapacity);
reply(entity, rsp);
return null;
}
}