package org.zstack.storage.primary.nfs; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.core.timeout.ApiTimeoutManager; import org.zstack.header.core.Completion; import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.host.HostConstant; import org.zstack.header.host.HostInventory; import org.zstack.header.message.MessageReply; import org.zstack.header.storage.backup.BackupStorageConstant; import org.zstack.header.storage.backup.BackupStorageInventory; import org.zstack.header.storage.primary.ImageCacheInventory; import org.zstack.header.storage.primary.PrimaryStorageInventory; import org.zstack.header.volume.VolumeInventory; import org.zstack.identity.AccountManager; import org.zstack.kvm.KVMConstant; import org.zstack.kvm.KVMHostAsyncHttpCallMsg; import org.zstack.kvm.KVMHostAsyncHttpCallReply; import org.zstack.storage.backup.BackupStoragePathMaker; import org.zstack.storage.backup.sftp.*; import org.zstack.storage.primary.nfs.NfsPrimaryStorageKVMBackendCommands.*; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import org.zstack.utils.path.PathUtil; import static org.zstack.core.Platform.operr; import java.util.List; import static org.zstack.utils.CollectionDSL.list; public class NfsPrimaryToSftpBackupKVMBackend implements NfsPrimaryToBackupStorageMediator { private static final CLogger logger = Utils.getLogger(NfsPrimaryToSftpBackupKVMBackend.class); @Autowired private CloudBus bus; @Autowired private NfsPrimaryStorageFactory primaryStorageFactory; @Autowired private AccountManager acntMgr; @Autowired private ErrorFacade errf; @Autowired private DatabaseFacade dbf; @Autowired private NfsPrimaryStorageManager nfsMgr; @Autowired private ApiTimeoutManager timeoutMgr; public static final String CREATE_VOLUME_FROM_TEMPLATE_PATH = "/nfsprimarystorage/sftp/createvolumefromtemplate"; public static final String UPLOAD_TO_SFTP_PATH = "/nfsprimarystorage/uploadtosftpbackupstorage"; public static final String DOWNLOAD_FROM_SFTP_PATH = "/nfsprimarystorage/downloadfromsftpbackupstorage"; @Override public String getSupportedPrimaryStorageType() { return NfsPrimaryStorageConstant.NFS_PRIMARY_STORAGE_TYPE; } @Override public String getSupportedBackupStorageType() { return SftpBackupStorageConstant.SFTP_BACKUP_STORAGE_TYPE; } @Override public List<String> getSupportedHypervisorTypes() { return list(KVMConstant.KVM_HYPERVISOR_TYPE); } @Override public void createVolumeFromImageCache(final PrimaryStorageInventory primaryStorage, final ImageCacheInventory image, final VolumeInventory volume, final ReturnValueCompletion<String> completion) { HostInventory host = primaryStorageFactory.getConnectedHostForOperation(primaryStorage); final String installPath = NfsPrimaryStorageKvmHelper.makeRootVolumeInstallUrl(primaryStorage, volume); final String accountUuid = acntMgr.getOwnerAccountUuidOfResource(volume.getUuid()); final CreateRootVolumeFromTemplateCmd cmd = new CreateRootVolumeFromTemplateCmd(); cmd.setTemplatePathInCache(image.getInstallUrl()); cmd.setInstallUrl(installPath); cmd.setAccountUuid(accountUuid); cmd.setName(volume.getName()); cmd.setVolumeUuid(volume.getUuid()); cmd.setUuid(primaryStorage.getUuid()); KVMHostAsyncHttpCallMsg msg = new KVMHostAsyncHttpCallMsg(); msg.setCommand(cmd); msg.setPath(CREATE_VOLUME_FROM_TEMPLATE_PATH); msg.setHostUuid(host.getUuid()); msg.setCommandTimeout(timeoutMgr.getTimeout(cmd.getClass(), "5m")); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, host.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } CreateRootVolumeFromTemplateResponse rsp = ((KVMHostAsyncHttpCallReply)reply).toResponse(CreateRootVolumeFromTemplateResponse.class); if (!rsp.isSuccess()) { ErrorCode err = operr("fails to create root volume[uuid:%s] from cached image[path:%s] because %s", volume.getUuid(), image.getImageUuid(), rsp.getError()); completion.fail(err); return; } nfsMgr.reportCapacityIfNeeded(primaryStorage.getUuid(), rsp); completion.success(installPath); } }); } @Override public void downloadBits(final PrimaryStorageInventory pinv, final BackupStorageInventory bsinv, final String backupStorageInstallPath, final String primaryStorageInstallPath, final Completion completion) { GetSftpBackupStorageDownloadCredentialMsg gmsg = new GetSftpBackupStorageDownloadCredentialMsg(); gmsg.setBackupStorageUuid(bsinv.getUuid()); bus.makeTargetServiceIdByResourceUuid(gmsg, BackupStorageConstant.SERVICE_ID, bsinv.getUuid()); bus.send(gmsg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } HostInventory host = primaryStorageFactory.getConnectedHostForOperation(pinv); final GetSftpBackupStorageDownloadCredentialReply greply = reply.castReply(); DownloadBitsFromSftpBackupStorageCmd cmd = new DownloadBitsFromSftpBackupStorageCmd(); cmd.setHostname(greply.getHostname()); cmd.setUsername(greply.getUsername()); cmd.setSshKey(greply.getSshKey()); cmd.setSshPort(greply.getSshPort()); cmd.setPrimaryStorageInstallPath(primaryStorageInstallPath); cmd.setBackupStorageInstallPath(backupStorageInstallPath); cmd.setUuid(pinv.getUuid()); KVMHostAsyncHttpCallMsg msg = new KVMHostAsyncHttpCallMsg(); msg.setCommand(cmd); msg.setPath(DOWNLOAD_FROM_SFTP_PATH); msg.setHostUuid(host.getUuid()); msg.setCommandTimeout(timeoutMgr.getTimeout(cmd.getClass(), "5m")); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, host.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } DownloadBitsFromSftpBackupStorageResponse rsp = ((KVMHostAsyncHttpCallReply)reply).toResponse(DownloadBitsFromSftpBackupStorageResponse.class); if (!rsp.isSuccess()) { completion.fail(operr("failed to download[%s] from SftpBackupStorage[hostname:%s] to nfs primary storage[uuid:%s, path:%s], %s", backupStorageInstallPath, greply.getHostname(), pinv.getUuid(), primaryStorageInstallPath, rsp.getError())); return; } nfsMgr.reportCapacityIfNeeded(pinv.getUuid(), rsp); completion.success(); } }); } }); } @Override public void uploadBits(final String imageUuid, final PrimaryStorageInventory pinv, BackupStorageInventory bsinv, final String backupStorageInstallPath, final String primaryStorageInstallPath, final ReturnValueCompletion<String> completion) { GetSftpBackupStorageDownloadCredentialMsg gmsg = new GetSftpBackupStorageDownloadCredentialMsg(); gmsg.setBackupStorageUuid(bsinv.getUuid()); bus.makeTargetServiceIdByResourceUuid(gmsg, BackupStorageConstant.SERVICE_ID, bsinv.getUuid()); bus.send(gmsg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } GetSftpBackupStorageDownloadCredentialReply r = reply.castReply(); upload(r.getHostname(), r.getSshKey(), r.getSshPort(), r.getUsername()); } private void upload(final String hostname, String sshKey, int sshPort, String username) { final HostInventory host = primaryStorageFactory.getConnectedHostForOperation(pinv); UploadToSftpCmd cmd = new UploadToSftpCmd(); cmd.setBackupStorageHostName(hostname); cmd.setBackupStorageUserName(username); cmd.setBackupStorageSshKey(sshKey); cmd.setBackupStorageSshPort(sshPort); cmd.setPrimaryStorageInstallPath(primaryStorageInstallPath); cmd.setBackupStorageInstallPath(backupStorageInstallPath); cmd.setUuid(pinv.getUuid()); KVMHostAsyncHttpCallMsg msg = new KVMHostAsyncHttpCallMsg(); msg.setCommand(cmd); msg.setPath(UPLOAD_TO_SFTP_PATH); msg.setHostUuid(host.getUuid()); msg.setCommandTimeout(timeoutMgr.getTimeout(cmd.getClass(), "5m")); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, host.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } UploadToSftpResponse rsp = ((KVMHostAsyncHttpCallReply)reply).toResponse(UploadToSftpResponse.class); if (!rsp.isSuccess()) { completion.fail(operr("failed to upload bits from nfs primary storage[uuid:%s, path:%s] to SFTP backup storage[hostname:%s, path: %s], %s", pinv.getUuid(), primaryStorageInstallPath, hostname, backupStorageInstallPath, rsp.getError())); return; } completion.success(backupStorageInstallPath); } }); } }); } private String findSftpRootPath(String bsUuid) { SimpleQuery<SftpBackupStorageVO> q = dbf.createQuery(SftpBackupStorageVO.class); q.select(SftpBackupStorageVO_.url); q.add(SftpBackupStorageVO_.uuid, SimpleQuery.Op.EQ, bsUuid); return q.findValue(); } @Override public String makeRootVolumeTemplateInstallPath(String bsUuid, String imageUuid) { return PathUtil.join( findSftpRootPath(bsUuid), BackupStoragePathMaker.makeRootVolumeTemplateInstallFolderPath(imageUuid), String.format("%s.qcow2", imageUuid) ); } @Override public String makeVolumeSnapshotInstallPath(String bsUuid, String snapshotUuid) { return PathUtil.join( findSftpRootPath(bsUuid), BackupStoragePathMaker.makeVolumeSnapshotInstallFolderPath(snapshotUuid), String.format("%s.qcow2", snapshotUuid) ); } @Override public String makeDataVolumeTemplateInstallPath(String backupStorageUuid, String volumeUuid) { return PathUtil.join( findSftpRootPath(backupStorageUuid), BackupStoragePathMaker.makeDataVolumeTemplateInstallFolderPath(volumeUuid), String.format("%s.qcow2", volumeUuid) ); } }