| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| |
| package org.apache.cloudstack.storage.datastore.driver; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import javax.inject.Inject; |
| |
| import org.apache.log4j.Logger; |
| import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; |
| import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; |
| import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; |
| import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; |
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; |
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; |
| import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; |
| import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; |
| import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; |
| import org.apache.cloudstack.framework.async.AsyncCompletionCallback; |
| import org.apache.cloudstack.storage.command.CommandResult; |
| import org.apache.cloudstack.storage.command.CreateObjectAnswer; |
| import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; |
| import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; |
| import org.apache.cloudstack.storage.datastore.util.ElastistorUtil; |
| import org.apache.cloudstack.storage.datastore.util.ElastistorUtil.FileSystem; |
| import org.apache.cloudstack.storage.to.SnapshotObjectTO; |
| import org.apache.cloudstack.storage.volume.VolumeObject; |
| |
| import com.cloud.agent.api.Answer; |
| import com.cloud.agent.api.to.DataObjectType; |
| import com.cloud.agent.api.to.DataStoreTO; |
| import com.cloud.agent.api.to.DataTO; |
| import com.cloud.storage.DiskOfferingVO; |
| import com.cloud.storage.ResizeVolumePayload; |
| import com.cloud.storage.Storage.StoragePoolType; |
| import com.cloud.storage.StorageManager; |
| import com.cloud.storage.StoragePool; |
| import com.cloud.storage.VolumeDetailVO; |
| import com.cloud.storage.VolumeVO; |
| import com.cloud.storage.dao.DiskOfferingDao; |
| import com.cloud.storage.dao.VolumeDao; |
| import com.cloud.storage.dao.VolumeDetailsDao; |
| import com.cloud.user.AccountManager; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| |
| /** |
| * The implementation class for <code>ElastistorPrimaryDataStoreDriver</code>. |
| * This directs the public interface methods to use CloudByte's Elastistor based |
| * volumes. |
| */ |
| public class ElastistorPrimaryDataStoreDriver extends CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver { |
| |
| private static final Logger s_logger = Logger.getLogger(ElastistorPrimaryDataStoreDriver.class); |
| |
| @Inject |
| AccountManager _accountMgr; |
| @Inject |
| DiskOfferingDao _diskOfferingDao; |
| @Inject |
| private VolumeDao _volumeDao; |
| @Inject |
| private PrimaryDataStoreDao _storagePoolDao; |
| @Inject |
| StorageManager storageMgr; |
| @Inject |
| VolumeDetailsDao _volumeDetailsDao; |
| |
| @Override |
| public DataTO getTO(DataObject data) { |
| return null; |
| } |
| |
| @Override |
| public DataStoreTO getStoreTO(DataStore store) { |
| return null; |
| } |
| |
| @Override |
| public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) { |
| |
| String iqn = null; |
| String errMsg = null; |
| |
| CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg)); |
| |
| if (dataObject.getType() == DataObjectType.VOLUME) { |
| |
| VolumeInfo volumeInfo = (VolumeInfo) dataObject; |
| |
| long storagePoolId = dataStore.getId(); |
| String volumeName = volumeInfo.getName(); |
| Long Iops = volumeInfo.getMaxIops(); |
| // quota size of the cloudbyte volume will be increased with the given HypervisorSnapshotReserve |
| Long quotaSize = getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, _storagePoolDao.findById(storagePoolId)); |
| |
| StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId()); |
| VolumeVO volume = _volumeDao.findById(volumeInfo.getId()); |
| |
| // if the primary storage is not managed (thick provisioned) |
| // then no need to create volume at elastistor, |
| // calling super(default) that creates a vdi(disk) only. |
| if (!(storagePool.isManaged())) { |
| super.createAsync(dataStore, dataObject, callback); |
| |
| // update the volume property |
| volume.setPoolType(storagePool.getPoolType()); |
| _volumeDao.update(volume.getId(), volume); |
| |
| return; |
| } |
| |
| DiskOfferingVO diskOffering = _diskOfferingDao.findById(volumeInfo.getDiskOfferingId()); |
| |
| long capacityIops = storagePool.getCapacityIops(); |
| capacityIops = capacityIops - Iops; |
| |
| if (capacityIops < 0) { |
| throw new CloudRuntimeException("IOPS not available. [pool:" + storagePool.getName() + "] [availiops:" + capacityIops + "] [requirediops:" + Iops + "]"); |
| } |
| |
| String protocoltype = null; |
| StoragePoolVO dataStoreVO = _storagePoolDao.findById(storagePoolId); |
| String desc = diskOffering.getDisplayText(); |
| |
| if (desc.toLowerCase().contains("iscsi")) { |
| protocoltype = "iscsi"; |
| } else if (dataStoreVO.getPoolType().equals(StoragePoolType.NetworkFilesystem) || dataStoreVO.getPoolType().equals(StoragePoolType.Filesystem)) { |
| protocoltype = "nfs"; |
| } else { |
| protocoltype = "iscsi"; |
| } |
| |
| FileSystem esvolume = null; |
| try { |
| esvolume = ElastistorUtil.createElastistorVolume(volumeName, dataStoreVO.getUuid(), quotaSize, Iops, protocoltype, volumeName); |
| } catch (Throwable e) { |
| s_logger.error(e.toString(), e); |
| result.setResult(e.toString()); |
| callback.complete(result); |
| throw new CloudRuntimeException(e.getMessage()); |
| } |
| |
| if (esvolume.getNfsenabled().equalsIgnoreCase("true")) { |
| volume.set_iScsiName(esvolume.getPath()); |
| volume.setPoolType(StoragePoolType.NetworkFilesystem); |
| } else { |
| iqn = esvolume.getIqn(); |
| String modifiediqn = "/" + iqn + "/0"; |
| volume.set_iScsiName(modifiediqn); |
| volume.setPoolType(StoragePoolType.IscsiLUN); |
| } |
| |
| volume.setFolder(String.valueOf(esvolume.getUuid())); |
| volume.setPoolId(storagePoolId); |
| volume.setUuid(esvolume.getUuid()); |
| volume.setPath(null); |
| |
| _volumeDao.update(volume.getId(), volume); |
| |
| // create new volume details for the volume |
| //updateVolumeDetails(volume, esvolume); |
| |
| long capacityBytes = storagePool.getCapacityBytes(); |
| long usedBytes = storagePool.getUsedBytes(); |
| |
| Long inbytes = volume.getSize(); |
| |
| usedBytes += inbytes; |
| |
| storagePool.setCapacityIops(capacityIops); |
| storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); |
| |
| _storagePoolDao.update(storagePoolId, storagePool); |
| s_logger.info("Elastistor volume creation complete."); |
| } else { |
| errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync"; |
| s_logger.error(errMsg); |
| } |
| |
| result.setResult(errMsg); |
| |
| callback.complete(result); |
| } |
| |
| @Override |
| public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CommandResult> callback) { |
| |
| String errMsg = null; |
| |
| StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId()); |
| |
| // if the primary storage is not managed(thick provisioned) then no need |
| // to delete volume at elastistor, just |
| // call the super(default) to delete a vdi(disk) only. |
| |
| if (!(storagePool.isManaged())) { |
| super.deleteAsync(dataStore, dataObject, callback); |
| return; |
| } |
| |
| if (dataObject.getType() == DataObjectType.VOLUME) { |
| VolumeInfo volumeInfo = (VolumeInfo) dataObject; |
| |
| long storagePoolId = dataStore.getId(); |
| boolean result = false; |
| try { |
| result = ElastistorUtil.deleteElastistorVolume(volumeInfo.getUuid()); |
| } catch (Throwable e) { |
| e.printStackTrace(); |
| CommandResult result2 = new CommandResult(); |
| result2.setResult(e.toString()); |
| callback.complete(result2); |
| } |
| |
| if (result) { |
| long usedBytes = storagePool.getUsedBytes(); |
| long capacityIops = storagePool.getCapacityIops(); |
| |
| usedBytes -= volumeInfo.getSize(); |
| capacityIops += volumeInfo.getMaxIops(); |
| |
| storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); |
| storagePool.setCapacityIops(capacityIops < 0 ? 0 : capacityIops); |
| |
| _storagePoolDao.update(storagePoolId, storagePool); |
| } else { |
| errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync"; |
| } |
| |
| } else { |
| errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync"; |
| } |
| |
| CommandResult result = new CommandResult(); |
| |
| result.setResult(errMsg); |
| |
| callback.complete(result); |
| } |
| |
| @Override |
| public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) { |
| throw new UnsupportedOperationException(); |
| |
| } |
| |
| @Override |
| public boolean canCopy(DataObject srcData, DataObject destData) { |
| return false; |
| } |
| |
| @Override |
| public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { |
| |
| s_logger.debug("Resize elastistor volume started"); |
| Boolean status = false; |
| VolumeObject vol = (VolumeObject) data; |
| StoragePool pool = (StoragePool) data.getDataStore(); |
| |
| ResizeVolumePayload resizeParameter = (ResizeVolumePayload) vol.getpayload(); |
| |
| CreateCmdResult result = new CreateCmdResult(null, null); |
| |
| StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); |
| |
| if (!(poolVO.isManaged())) { |
| super.resize(data, callback); |
| return; |
| } |
| |
| try { |
| |
| status = ElastistorUtil.updateElastistorVolumeSize(vol.getUuid(), resizeParameter.newSize); |
| |
| } catch (Throwable e) { |
| s_logger.error("Resize elastistor volume failed, please contact elastistor admin.", e); |
| result.setResult(e.toString()); |
| callback.complete(result); |
| } |
| |
| if (status) { |
| // now updating the cloudstack storagepool usedbytes and volume |
| Long usedBytes = poolVO.getUsedBytes(); |
| Long currentVolumeSize = vol.getSize(); |
| Long newUsedBytes; |
| |
| if (currentVolumeSize < resizeParameter.newSize) { |
| newUsedBytes = usedBytes + (resizeParameter.newSize - currentVolumeSize); |
| poolVO.setUsedBytes(newUsedBytes); |
| } else { |
| newUsedBytes = usedBytes - (currentVolumeSize - resizeParameter.newSize); |
| poolVO.setUsedBytes(newUsedBytes); |
| } |
| |
| _storagePoolDao.update(pool.getId(), poolVO); |
| |
| vol.getVolume().setSize(resizeParameter.newSize); |
| vol.update(); |
| callback.complete(result); |
| } else { |
| callback.complete(result); |
| } |
| |
| } |
| |
| //this method will utilize the volume details table to add third party volume properties |
| public void updateVolumeDetails(VolumeVO volume, FileSystem esvolume) { |
| |
| VolumeDetailVO compression = new VolumeDetailVO(volume.getId(), "compression", esvolume.getCompression(), false); |
| _volumeDetailsDao.persist(compression); |
| VolumeDetailVO deduplication = new VolumeDetailVO(volume.getId(), "deduplication", esvolume.getDeduplication(), false); |
| _volumeDetailsDao.persist(deduplication); |
| VolumeDetailVO sync = new VolumeDetailVO(volume.getId(), "sync", esvolume.getSync(), false); |
| _volumeDetailsDao.persist(sync); |
| VolumeDetailVO graceallowed = new VolumeDetailVO(volume.getId(), "graceallowed", esvolume.getGraceallowed(), false); |
| _volumeDetailsDao.persist(graceallowed); |
| |
| } |
| |
| @Override |
| public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool pool) { |
| VolumeInfo volume = (VolumeInfo)dataObject; |
| long volumeSize = volume.getSize(); |
| Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve(); |
| |
| if (hypervisorSnapshotReserve != null) { |
| if (hypervisorSnapshotReserve < 25) { |
| hypervisorSnapshotReserve = 25; |
| } |
| |
| volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f); |
| } |
| |
| return volumeSize; |
| } |
| |
| @Override |
| public ChapInfo getChapInfo(DataObject dataObject) { |
| return null; |
| } |
| |
| @Override |
| public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { |
| CreateCmdResult result = null; |
| try { |
| s_logger.info("taking elastistor volume snapshot"); |
| SnapshotObjectTO snapshotTO = (SnapshotObjectTO)snapshot.getTO(); |
| |
| String volumeid = snapshotTO.getVolume().getUuid(); |
| String snapshotname = snapshotTO.getName(); |
| |
| Answer answer = ElastistorUtil.createElastistorVolumeSnapshot(volumeid, snapshotname); |
| |
| if(answer.getResult() == false){ |
| s_logger.info("elastistor volume snapshot failed"); |
| throw new CloudRuntimeException("elastistor volume snapshot failed"); |
| }else{ |
| s_logger.info("elastistor volume snapshot succesfull"); |
| |
| snapshotTO.setPath(answer.getDetails()); |
| |
| CreateObjectAnswer createObjectAnswer = new CreateObjectAnswer(snapshotTO); |
| |
| result = new CreateCmdResult(null, createObjectAnswer); |
| |
| result.setResult(null); |
| } |
| } |
| catch (Throwable e) { |
| s_logger.debug("Failed to take snapshot: " + e.getMessage()); |
| result = new CreateCmdResult(null, null); |
| result.setResult(e.toString()); |
| } |
| callback.complete(result); |
| } |
| |
| @Override |
| public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Map<String, String> getCapabilities() { |
| Map<String, String> mapCapabilities = new HashMap<String, String>(); |
| |
| mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString()); |
| |
| return mapCapabilities; |
| } |
| |
| } |