/* * 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 java.util.UUID; 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.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; 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.StorageAction; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.RevertSnapshotCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.volume.VolumeObject; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.ResizeVolumeAnswer; import com.cloud.agent.api.storage.ResizeVolumeCommand; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.configuration.Config; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.Host; import com.cloud.host.dao.HostDao; import com.cloud.storage.CreateSnapshotPayload; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ResizeVolumePayload; import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.TemplateManager; import com.cloud.utils.NumbersUtil; import com.cloud.vm.dao.VMInstanceDao; public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver { @Override public Map<String, String> getCapabilities() { Map<String, String> caps = new HashMap<String, String>(); caps.put(DataStoreCapabilities.VOLUME_SNAPSHOT_QUIESCEVM.toString(), "false"); return caps; } private static final Logger s_logger = Logger.getLogger(CloudStackPrimaryDataStoreDriverImpl.class); @Inject DiskOfferingDao diskOfferingDao; @Inject VMTemplateDao templateDao; @Inject VolumeDao volumeDao; @Inject HostDao hostDao; @Inject StorageManager storageMgr; @Inject VMInstanceDao vmDao; @Inject SnapshotDao snapshotDao; @Inject PrimaryDataStoreDao primaryStoreDao; @Inject SnapshotManager snapshotMgr; @Inject EndPointSelector epSelector; @Inject ConfigurationDao configDao; @Inject TemplateManager templateManager; @Inject TemplateDataFactory templateDataFactory; @Override public DataTO getTO(DataObject data) { return null; } @Override public DataStoreTO getStoreTO(DataStore store) { return null; } public Answer createVolume(VolumeInfo volume) throws StorageUnavailableException { if (s_logger.isDebugEnabled()) { s_logger.debug("Creating volume: " + volume); } CreateObjectCommand cmd = new CreateObjectCommand(volume.getTO()); EndPoint ep = epSelector.select(volume); Answer answer = null; if (ep == null) { String errMsg = "No remote endpoint to send DeleteCommand, check if host or ssvm is down?"; s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { answer = ep.sendMessage(cmd); } return answer; } @Override public ChapInfo getChapInfo(DataObject dataObject) { return null; } @Override public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) { return false; } @Override public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) { } @Override public long getUsedBytes(StoragePool storagePool) { return 0; } @Override public long getUsedIops(StoragePool storagePool) { return 0; } @Override public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool pool) { return dataObject.getSize(); } @Override public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) { return 0; } @Override public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { String errMsg = null; Answer answer = null; CreateCmdResult result = new CreateCmdResult(null, null); if (data.getType() == DataObjectType.VOLUME) { try { answer = createVolume((VolumeInfo) data); if ((answer == null) || (!answer.getResult())) { result.setSuccess(false); if (answer != null) { result.setResult(answer.getDetails()); } } else { result.setAnswer(answer); } } catch (StorageUnavailableException e) { s_logger.debug("failed to create volume", e); errMsg = e.toString(); } catch (Exception e) { s_logger.debug("failed to create volume", e); errMsg = e.toString(); } } if (errMsg != null) { result.setResult(errMsg); } callback.complete(result); } @Override public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CommandResult> callback) { DeleteCommand cmd = new DeleteCommand(data.getTO()); CommandResult result = new CommandResult(); try { EndPoint ep = null; if (data.getType() == DataObjectType.VOLUME) { ep = epSelector.select(data, StorageAction.DELETEVOLUME); } else { ep = epSelector.select(data); } if (ep == null) { String errMsg = "No remote endpoint to send DeleteCommand, check if host or ssvm is down?"; s_logger.error(errMsg); result.setResult(errMsg); } else { Answer answer = ep.sendMessage(cmd); if (answer != null && !answer.getResult()) { result.setResult(answer.getDetails()); } } } catch (Exception ex) { s_logger.debug("Unable to destoy volume" + data.getId(), ex); result.setResult(ex.toString()); } callback.complete(result); } @Override public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) { DataStore store = destData.getDataStore(); if (store.getRole() == DataStoreRole.Primary) { if ((srcdata.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.TEMPLATE)) { //For CLVM, we need to copy template to primary storage at all, just fake the copy result. TemplateObjectTO templateObjectTO = new TemplateObjectTO(); templateObjectTO.setPath(UUID.randomUUID().toString()); templateObjectTO.setSize(srcdata.getSize()); templateObjectTO.setPhysicalSize(srcdata.getSize()); templateObjectTO.setFormat(Storage.ImageFormat.RAW); CopyCmdAnswer answer = new CopyCmdAnswer(templateObjectTO); CopyCommandResult result = new CopyCommandResult("", answer); callback.complete(result); } else if (srcdata.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.VOLUME) { //For CLVM, we need to pass template on secondary storage to hypervisor String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString()); int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue())); StoragePoolVO storagePoolVO = primaryStoreDao.findById(store.getId()); DataStore imageStore = templateManager.getImageStore(storagePoolVO.getDataCenterId(), srcdata.getId()); DataObject srcData = templateDataFactory.getTemplate(srcdata.getId(), imageStore); CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _primaryStorageDownloadWait, true); EndPoint ep = epSelector.select(srcData, destData); Answer answer = null; if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { answer = ep.sendMessage(cmd); } CopyCommandResult result = new CopyCommandResult("", answer); callback.complete(result); } } } @Override public boolean canCopy(DataObject srcData, DataObject destData) { //BUG fix for CLOUDSTACK-4618 DataStore store = destData.getDataStore(); if (store.getRole() == DataStoreRole.Primary && srcData.getType() == DataObjectType.TEMPLATE && (destData.getType() == DataObjectType.TEMPLATE || destData.getType() == DataObjectType.VOLUME)) { StoragePoolVO storagePoolVO = primaryStoreDao.findById(store.getId()); if (storagePoolVO != null && storagePoolVO.getPoolType() == Storage.StoragePoolType.CLVM) { return true; } } return false; } @Override public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { CreateCmdResult result = null; try { SnapshotObjectTO snapshotTO = (SnapshotObjectTO) snapshot.getTO(); Object payload = snapshot.getPayload(); if (payload != null && payload instanceof CreateSnapshotPayload) { CreateSnapshotPayload snapshotPayload = (CreateSnapshotPayload) payload; snapshotTO.setQuiescevm(snapshotPayload.getQuiescevm()); } CreateObjectCommand cmd = new CreateObjectCommand(snapshotTO); EndPoint ep = epSelector.select(snapshot, StorageAction.TAKESNAPSHOT); Answer answer = null; if (ep == null) { String errMsg = "No remote endpoint to send createObjectCommand, check if host or ssvm is down?"; s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { answer = ep.sendMessage(cmd); } result = new CreateCmdResult(null, answer); if (answer != null && !answer.getResult()) { result.setResult(answer.getDetails()); } callback.complete(result); return; } catch (Exception e) { s_logger.debug("Failed to take snapshot: " + snapshot.getId(), e); result = new CreateCmdResult(null, null); result.setResult(e.toString()); } callback.complete(result); } @Override public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { SnapshotObjectTO snapshotTO = (SnapshotObjectTO)snapshot.getTO(); RevertSnapshotCommand cmd = new RevertSnapshotCommand(snapshotTO); CommandResult result = new CommandResult(); try { EndPoint ep = epSelector.select(snapshotOnPrimaryStore); if ( ep == null ){ String errMsg = "No remote endpoint to send RevertSnapshotCommand, check if host or ssvm is down?"; s_logger.error(errMsg); result.setResult(errMsg); } else { Answer answer = ep.sendMessage(cmd); if (answer != null && !answer.getResult()) { result.setResult(answer.getDetails()); } } } catch (Exception ex) { s_logger.debug("Unable to revert snapshot " + snapshot.getId(), ex); result.setResult(ex.toString()); } callback.complete(result); } @Override public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { VolumeObject vol = (VolumeObject) data; StoragePool pool = (StoragePool) data.getDataStore(); ResizeVolumePayload resizeParameter = (ResizeVolumePayload) vol.getpayload(); ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), resizeParameter.newSize, resizeParameter.shrinkOk, resizeParameter.instanceName); CreateCmdResult result = new CreateCmdResult(null, null); try { ResizeVolumeAnswer answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, resizeParameter.hosts, resizeCmd); if (answer != null && answer.getResult()) { long finalSize = answer.getNewSize(); s_logger.debug("Resize: volume started at size " + vol.getSize() + " and ended at size " + finalSize); vol.setSize(finalSize); vol.update(); } else if (answer != null) { result.setResult(answer.getDetails()); } else { s_logger.debug("return a null answer, mark it as failed for unknown reason"); result.setResult("return a null answer, mark it as failed for unknown reason"); } } catch (Exception e) { s_logger.debug("sending resize command failed", e); result.setResult(e.toString()); } callback.complete(result); } }