blob: 74fbde52736444ed29e38a5b42d081c820d8f583 [file] [log] [blame]
//
// 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;
}
}