package org.zstack.storage.ceph.primary; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; 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.Platform; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.rest.RESTConstant; import org.zstack.header.rest.RESTFacade; import org.zstack.header.storage.backup.BackupStorageVO_; import org.zstack.header.storage.primary.PrimaryStorageVO; import org.zstack.kvm.KVMAgentCommands; import org.zstack.storage.ceph.primary.CephPrimaryStorageBase.*; import org.zstack.storage.ceph.primary.CephPrimaryStorageMonBase.PingCmd; import org.zstack.storage.ceph.primary.CephPrimaryStorageMonBase.PingRsp; import org.zstack.storage.ceph.primary.CephPrimaryStorageSimulatorConfig.CephPrimaryStorageConfig; import org.zstack.utils.Utils; import org.zstack.utils.gson.JSONObjectUtil; import org.zstack.utils.logging.CLogger; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * Created by frank on 7/28/2015. */ @Controller public class CephPrimaryStorageSimulator { CLogger logger = Utils.getLogger(CephPrimaryStorageSimulator.class); @Autowired private DatabaseFacade dbf; @Autowired private RESTFacade restf; @Autowired private CephPrimaryStorageSimulatorConfig config; private Map<String, Long> bitSizeMap = new HashMap<String, Long>(); public void reply(HttpEntity<String> entity, Object rsp) { String taskUuid = entity.getHeaders().getFirst(RESTConstant.TASK_UUID); String callbackUrl = entity.getHeaders().getFirst(RESTConstant.CALLBACK_URL); String rspBody = JSONObjectUtil.toJsonString(rsp); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setContentLength(rspBody.length()); headers.set(RESTConstant.TASK_UUID, taskUuid); HttpEntity<String> rreq = new HttpEntity<String>(rspBody, headers); restf.getRESTTemplate().exchange(callbackUrl, HttpMethod.POST, rreq, String.class); } private CephPrimaryStorageConfig getConfig(AgentCommand cmd) { SimpleQuery<PrimaryStorageVO> q = dbf.createQuery(PrimaryStorageVO.class); q.select(BackupStorageVO_.name); q.add(BackupStorageVO_.uuid, Op.EQ, cmd.getUuid()); String name = q.findValue(); CephPrimaryStorageConfig c = config.config.get(name); if (c == null) { throw new CloudRuntimeException(String.format("cannot find CephPrimaryStorageConfig by name[%s], uuid[%s]", name, cmd.getUuid())); } return c; } @RequestMapping(value= CephPrimaryStorageBase.GET_FACTS, method= RequestMethod.POST) public @ResponseBody String getFacts(HttpEntity<String> entity) { GetFactsCmd cmd = JSONObjectUtil.toObject(entity.getBody(), GetFactsCmd.class); GetFactsRsp rsp = new GetFactsRsp(); config.getFactsCmds.add(cmd); String fsid = config.getFactsCmdFsid.get(cmd.monUuid); if (fsid == null) { CephPrimaryStorageConfig c = getConfig(cmd); fsid = c.fsid; } rsp.fsid = fsid; rsp.monAddr = config.monAddr.get(cmd.monUuid); reply(entity, rsp); return null; } @RequestMapping(value= CephPrimaryStorageBase.DELETE_POOL_PATH, method= RequestMethod.POST) public @ResponseBody String deletePool(HttpEntity<String> entity) { DeletePoolCmd cmd = JSONObjectUtil.toObject(entity.getBody(), DeletePoolCmd.class); config.deletePoolCmds.add(cmd); reply(entity, new DeletePoolRsp()); return null; } @RequestMapping(value= CephPrimaryStorageBase.INIT_PATH, method= RequestMethod.POST) public @ResponseBody String initialize(HttpEntity<String> entity) { InitCmd cmd = JSONObjectUtil.toObject(entity.getBody(), InitCmd.class); CephPrimaryStorageConfig cpc = getConfig(cmd); InitRsp rsp = new InitRsp(); if (!config.monInitSuccess) { rsp.error = "on purpose"; rsp.success = false; } else { rsp.fsid = cpc.fsid; rsp.userKey = Platform.getUuid(); rsp.totalCapacity = cpc.totalCapacity; rsp.availableCapacity = cpc.availCapacity; } reply(entity, rsp); return null; } private void setCapacity(AgentCommand cmd, AgentResponse rsp, long size) { CephPrimaryStorageConfig cpc = getConfig(cmd); rsp.totalCapacity = cpc.totalCapacity; rsp.availableCapacity = cpc.availCapacity + size; } @RequestMapping(value= CephPrimaryStorageMonBase.PING_PATH, method= RequestMethod.POST) public @ResponseBody String pingMon(HttpEntity<String> entity) { PingCmd cmd = JSONObjectUtil.toObject(entity.getBody(), PingCmd.class); Boolean success = config.pingCmdSuccess.get(cmd.monUuid); PingRsp rsp = new PingRsp(); rsp.success = success == null ? true : success; if (!rsp.success) { rsp.error = "on purpose"; } rsp.failure = config.pingCmdOperationFailure.get(cmd.monUuid); reply(entity, rsp); return null; } @RequestMapping(value= CephPrimaryStorageBase.CREATE_VOLUME_PATH, method= RequestMethod.POST) public @ResponseBody String createEmptyVolume(HttpEntity<String> entity) throws InterruptedException { if (config.synchronizedCreateEmptyVolume) { synchronized (config) { doCreateEmptyVolume(entity); } } else { doCreateEmptyVolume(entity); } return null; } String doCreateEmptyVolume(HttpEntity<String> entity) throws InterruptedException { CreateEmptyVolumeCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CreateEmptyVolumeCmd.class); config.createEmptyVolumeCmds.add(cmd); CreateEmptyVolumeRsp rsp = new CreateEmptyVolumeRsp(); //setCapacity(cmd, rsp, -cmd.getSize()); for (String path : bitSizeMap.keySet()) { if (!path.equals(cmd.getInstallPath())) { continue; } rsp.error = String.format("File exists[%s]", cmd.getInstallPath()); rsp.setSuccess(false); reply(entity, rsp); return null; } TimeUnit.SECONDS.sleep(2); bitSizeMap.put(cmd.getInstallPath(), cmd.getSize()); reply(entity, rsp); return null; } @RequestMapping(value= CephPrimaryStorageBase.KVM_CREATE_SECRET_PATH, method= RequestMethod.POST) public @ResponseBody String createKvmSecret(HttpEntity<String> entity) { CreateKvmSecretCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CreateKvmSecretCmd.class); config.createKvmSecretCmds.add(cmd); reply(entity, new KVMAgentCommands.AgentResponse()); return null; } @RequestMapping(value= CephPrimaryStorageBase.DELETE_PATH, method= RequestMethod.POST) public @ResponseBody String doDelete(HttpEntity<String> entity) { DeleteCmd cmd = JSONObjectUtil.toObject(entity.getBody(), DeleteCmd.class); config.deleteCmds.add(cmd); Long size = bitSizeMap.get(cmd.getInstallPath()); size = size == null ? 0 : size; DeleteRsp rsp = new DeleteRsp(); setCapacity(cmd, rsp, size); reply(entity, rsp); return null; } @RequestMapping(value= CephPrimaryStorageBase.CREATE_SNAPSHOT_PATH, method= RequestMethod.POST) public @ResponseBody String createSnapshot(HttpEntity<String> entity) { CreateSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CreateSnapshotCmd.class); config.createSnapshotCmds.add(cmd); CreateSnapshotRsp rsp = new CreateSnapshotRsp(); Long size = config.createSnapshotCmdSize.get(cmd.getVolumeUuid()); rsp.setActualSize(size == null ? 0 : size); reply(entity, rsp); return null; } @RequestMapping(value= CephPrimaryStorageBase.DELETE_SNAPSHOT_PATH, method= RequestMethod.POST) public @ResponseBody String deleteSnapshot(HttpEntity<String> entity) { DeleteSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), DeleteSnapshotCmd.class); config.deleteSnapshotCmds.add(cmd); reply(entity, new DeleteSnapshotRsp()); return null; } @RequestMapping(value= CephPrimaryStorageBase.PROTECT_SNAPSHOT_PATH, method= RequestMethod.POST) public @ResponseBody String protectSnapshot(HttpEntity<String> entity) { ProtectSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), ProtectSnapshotCmd.class); config.protectSnapshotCmds.add(cmd); reply(entity, new ProtectSnapshotRsp()); return null; } @RequestMapping(value= CephPrimaryStorageBase.UNPROTECT_SNAPSHOT_PATH, method= RequestMethod.POST) public @ResponseBody String unprotectSnapshot(HttpEntity<String> entity) { UnprotectedSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), UnprotectedSnapshotCmd.class); config.unprotectedSnapshotCmds.add(cmd); reply(entity, new UnprotectedSnapshotRsp()); return null; } @RequestMapping(value= CephPrimaryStorageBase.CLONE_PATH, method= RequestMethod.POST) public @ResponseBody String clone(HttpEntity<String> entity) { CloneCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CloneCmd.class); config.cloneCmds.add(cmd); CloneRsp rsp = new CloneRsp(); reply(entity, rsp); return null; } @RequestMapping(value= CephPrimaryStorageBase.FLATTEN_PATH, method= RequestMethod.POST) public @ResponseBody String flatten(HttpEntity<String> entity) { FlattenCmd cmd = JSONObjectUtil.toObject(entity.getBody(), FlattenCmd.class); config.flattenCmds.add(cmd); reply(entity, new FlattenRsp()); return null; } @RequestMapping(value= CephPrimaryStorageBase.CP_PATH, method= RequestMethod.POST) public @ResponseBody String cp(HttpEntity<String> entity) { CpRsp rsp = new CpRsp(); CpCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CpCmd.class); config.cpCmds.add(cmd); Long size = config.cpCmdSize.get(cmd.resourceUuid); rsp.size = size == null ? 0 : size; Long asize = config.cpCmdActualSize.get(cmd.resourceUuid); rsp.actualSize = asize == null ? 0 : asize; reply(entity, rsp); return null; } @RequestMapping(value= CephPrimaryStorageBase.GET_VOLUME_SIZE_PATH, method= RequestMethod.POST) public @ResponseBody String getVolumeSize(HttpEntity<String> entity) { GetVolumeSizeCmd cmd = JSONObjectUtil.toObject(entity.getBody(), GetVolumeSizeCmd.class); config.getVolumeSizeCmds.add(cmd); Long asize = config.getVolumeSizeCmdActualSize.get(cmd.volumeUuid); GetVolumeSizeRsp rsp = new GetVolumeSizeRsp(); 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= CephPrimaryStorageBase.SFTP_UPLOAD_PATH, method= RequestMethod.POST) public @ResponseBody String sftpUpload(HttpEntity<String> entity) { SftpUpLoadCmd cmd = JSONObjectUtil.toObject(entity.getBody(), SftpUpLoadCmd.class); config.sftpUpLoadCmds.add(cmd); reply(entity, new SftpUpLoadCmd()); return null; } @RequestMapping(value= CephPrimaryStorageBase.SFTP_DOWNLOAD_PATH, method= RequestMethod.POST) public @ResponseBody String sftpDownload(HttpEntity<String> entity) { SftpDownloadCmd cmd = JSONObjectUtil.toObject(entity.getBody(), SftpDownloadCmd.class); config.sftpDownloadCmds.add(cmd); reply(entity, new SftpDownloadRsp()); return null; } @RequestMapping(value= CephPrimaryStorageBase.ROLLBACK_SNAPSHOT_PATH, method= RequestMethod.POST) public @ResponseBody String rollback(HttpEntity<String> entity) { RollbackSnapshotCmd cmd = JSONObjectUtil.toObject(entity.getBody(), RollbackSnapshotCmd.class); config.rollbackSnapshotCmds.add(cmd); reply(entity, new RollbackSnapshotRsp()); return null; } @RequestMapping(value= CephPrimaryStorageBase.KVM_HA_SETUP_SELF_FENCER, method= RequestMethod.POST) public @ResponseBody String setupSelfFencer(HttpEntity<String> entity) { KvmSetupSelfFencerCmd cmd = JSONObjectUtil.toObject(entity.getBody(), KvmSetupSelfFencerCmd.class); config.kvmSetupSelfFencerCmds.add(cmd); reply(entity, new AgentResponse()); return null; } @RequestMapping(value= CephPrimaryStorageBase.KVM_HA_CANCEL_SELF_FENCER, method= RequestMethod.POST) public @ResponseBody String cancelSelfFencer(HttpEntity<String> entity) { KvmCancelSelfFencerCmd cmd = JSONObjectUtil.toObject(entity.getBody(), KvmCancelSelfFencerCmd.class); config.kvmCancelSelfFencerCmds.add(cmd); reply(entity, new AgentResponse()); return null; } @RequestMapping(value= CephPrimaryStorageBase.DELETE_IMAGE_CACHE, method= RequestMethod.POST) public @ResponseBody String deleteImageCache(HttpEntity<String> entity) { DeleteImageCacheCmd cmd = JSONObjectUtil.toObject(entity.getBody(), DeleteImageCacheCmd.class); config.deleteImageCacheCmds.add(cmd); reply(entity, new AgentResponse()); return null; } @RequestMapping(value= CephPrimaryStorageBase.CHECK_BITS_PATH, method= RequestMethod.POST) public @ResponseBody String checkBitsPath(HttpEntity<String> entity) { CheckIsBitsExistingCmd cmd = JSONObjectUtil.toObject(entity.getBody(), CheckIsBitsExistingCmd.class); config.checkBitsPathCmds.add(cmd); CheckIsBitsExistingRsp rsp = new CheckIsBitsExistingRsp(); rsp.setExisting(true); reply(entity, rsp); return null; } }