| /* |
| * 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.List; |
| import java.util.Map; |
| |
| import javax.inject.Inject; |
| |
| 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.DataStoreManager; |
| 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.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.RemoteHostEndPoint; |
| import org.apache.cloudstack.storage.command.CommandResult; |
| import org.apache.cloudstack.storage.command.CopyCmdAnswer; |
| import org.apache.cloudstack.storage.command.CreateObjectAnswer; |
| import org.apache.cloudstack.storage.command.StorageSubSystemCommand; |
| import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; |
| import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; |
| import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; |
| import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; |
| import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; |
| import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; |
| import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; |
| import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; |
| import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; |
| import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; |
| import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; |
| import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; |
| import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; |
| import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; |
| import org.apache.cloudstack.storage.to.SnapshotObjectTO; |
| import org.apache.cloudstack.storage.to.TemplateObjectTO; |
| import org.apache.cloudstack.storage.to.VolumeObjectTO; |
| import org.apache.cloudstack.storage.volume.VolumeObject; |
| import org.apache.commons.collections4.CollectionUtils; |
| import org.apache.commons.collections4.MapUtils; |
| import org.apache.log4j.Logger; |
| |
| import com.cloud.agent.api.Answer; |
| import com.cloud.agent.api.storage.ResizeVolumeAnswer; |
| import com.cloud.agent.api.storage.StorPoolBackupSnapshotCommand; |
| import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand; |
| import com.cloud.agent.api.storage.StorPoolCopyVolumeToSecondaryCommand; |
| import com.cloud.agent.api.storage.StorPoolDownloadTemplateCommand; |
| import com.cloud.agent.api.storage.StorPoolDownloadVolumeCommand; |
| import com.cloud.agent.api.storage.StorPoolResizeVolumeCommand; |
| import com.cloud.agent.api.storage.StorPoolSetVolumeEncryptionAnswer; |
| import com.cloud.agent.api.storage.StorPoolSetVolumeEncryptionCommand; |
| 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.dc.dao.ClusterDao; |
| import com.cloud.host.Host; |
| import com.cloud.host.HostVO; |
| import com.cloud.host.dao.HostDao; |
| import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; |
| import com.cloud.server.ResourceTag; |
| import com.cloud.server.ResourceTag.ResourceObjectType; |
| import com.cloud.storage.DataStoreRole; |
| 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.VMTemplateDetailVO; |
| import com.cloud.storage.VMTemplateStoragePoolVO; |
| import com.cloud.storage.Volume; |
| import com.cloud.storage.VolumeDetailVO; |
| import com.cloud.storage.VolumeVO; |
| import com.cloud.storage.dao.SnapshotDetailsDao; |
| import com.cloud.storage.dao.SnapshotDetailsVO; |
| import com.cloud.storage.dao.StoragePoolHostDao; |
| import com.cloud.storage.dao.VMTemplateDetailsDao; |
| import com.cloud.storage.dao.VolumeDao; |
| import com.cloud.storage.dao.VolumeDetailsDao; |
| import com.cloud.tags.dao.ResourceTagDao; |
| import com.cloud.utils.Pair; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.vm.VMInstanceVO; |
| import com.cloud.vm.VirtualMachine.State; |
| import com.cloud.vm.VirtualMachineManager; |
| import com.cloud.vm.dao.VMInstanceDao; |
| |
| public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { |
| |
| private static final Logger log = Logger.getLogger(StorPoolPrimaryDataStoreDriver.class); |
| |
| @Inject |
| private VolumeDao volumeDao; |
| @Inject |
| private StorageManager storageMgr; |
| @Inject |
| private PrimaryDataStoreDao primaryStoreDao; |
| @Inject |
| private EndPointSelector selector; |
| @Inject |
| private ConfigurationDao configDao; |
| @Inject |
| private TemplateDataStoreDao vmTemplateDataStoreDao; |
| @Inject |
| private VMInstanceDao vmInstanceDao; |
| @Inject |
| private ClusterDao clusterDao; |
| @Inject |
| private HostDao hostDao; |
| @Inject |
| private ResourceTagDao _resourceTagDao; |
| @Inject |
| private SnapshotDetailsDao _snapshotDetailsDao; |
| @Inject |
| private SnapshotDataStoreDao snapshotDataStoreDao; |
| @Inject |
| private VolumeDetailsDao volumeDetailsDao; |
| @Inject |
| private VMTemplateDetailsDao vmTemplateDetailsDao; |
| @Inject |
| private StoragePoolDetailsDao storagePoolDetailsDao; |
| @Inject |
| private StoragePoolHostDao storagePoolHostDao; |
| @Inject |
| DataStoreManager dataStoreManager; |
| |
| private SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) { |
| List<SnapshotDataStoreVO> snaps = snapshotDataStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image); |
| for (SnapshotDataStoreVO ref : snaps) { |
| if (zoneId == dataStoreManager.getStoreZoneId(ref.getDataStoreId(), ref.getRole())) { |
| return ref; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Map<String, String> getCapabilities() { |
| return null; |
| } |
| |
| @Override |
| public DataTO getTO(DataObject data) { |
| return null; |
| } |
| |
| @Override |
| public DataStoreTO getStoreTO(DataStore store) { |
| return null; |
| } |
| |
| @Override |
| public long getUsedBytes(StoragePool storagePool) { |
| return 0; |
| } |
| |
| @Override |
| public long getUsedIops(StoragePool storagePool) { |
| return 0; |
| } |
| |
| @Override |
| public boolean grantAccess(DataObject data, Host host, DataStore dataStore) { |
| return false; |
| } |
| |
| @Override |
| public void revokeAccess(DataObject data, Host host, DataStore dataStore) { |
| if (DataObjectType.VOLUME == data.getType()) { |
| final VolumeVO volume = volumeDao.findById(data.getId()); |
| if (volume.getInstanceId() == null) { |
| StorPoolUtil.spLog("Removing tags from detached volume=%s", volume.toString()); |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); |
| StorPoolUtil.volumeRemoveTags(StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true), conn); |
| } |
| } |
| |
| } |
| |
| private void updateStoragePool(final long poolId, final long deltaUsedBytes) { |
| StoragePoolVO storagePool = primaryStoreDao.findById(poolId); |
| final long capacity = storagePool.getCapacityBytes(); |
| final long used = storagePool.getUsedBytes() + deltaUsedBytes; |
| |
| storagePool.setUsedBytes(used < 0 ? 0 : (used > capacity ? capacity : used)); |
| primaryStoreDao.update(poolId, storagePool); |
| } |
| |
| private String getVMInstanceUUID(Long id) { |
| return id != null ? vmInstanceDao.findById(id).getUuid() : null; |
| } |
| |
| protected void _completeResponse(final CreateObjectAnswer answer, final String err, final AsyncCompletionCallback<CommandResult> callback) |
| { |
| final CreateCmdResult res = new CreateCmdResult(null, answer); |
| res.setResult(err); |
| callback.complete(res); |
| } |
| |
| protected void completeResponse(final DataTO result, final AsyncCompletionCallback<CommandResult> callback) |
| { |
| _completeResponse(new CreateObjectAnswer(result), null, callback); |
| } |
| |
| protected void completeResponse(final String err, final AsyncCompletionCallback<CommandResult> callback) |
| { |
| _completeResponse(new CreateObjectAnswer(err), err, callback); |
| } |
| |
| @Override |
| public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool pool) { |
| return dataObject.getSize(); |
| } |
| |
| @Override |
| public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) { |
| return 0; |
| } |
| |
| @Override |
| public ChapInfo getChapInfo(DataObject dataObject) { |
| return null; |
| } |
| |
| @Override |
| public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { |
| String path = null; |
| Answer answer; |
| if (data.getType() == DataObjectType.VOLUME) { |
| try { |
| VolumeInfo vinfo = (VolumeInfo)data; |
| String name = vinfo.getUuid(); |
| Long size = vinfo.getPassphraseId() == null ? vinfo.getSize() : vinfo.getSize() + 2097152; |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); |
| |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", vinfo.getName(), vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); |
| SpApiResponse resp = StorPoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vinfo.getInstanceId()), null, "volume", vinfo.getMaxIops(), conn); |
| if (resp.getError() == null) { |
| String volumeName = StorPoolUtil.getNameFromResponse(resp, false); |
| path = StorPoolUtil.devPath(volumeName); |
| |
| updateVolume(dataStore, path, vinfo); |
| |
| if (vinfo.getPassphraseId() != null) { |
| VolumeObjectTO volume = updateVolumeObjectTO(vinfo, resp); |
| answer = createEncryptedVolume(dataStore, data, vinfo, size, volume, null, true); |
| } else { |
| answer = new Answer(null, true, null); |
| } |
| updateStoragePool(dataStore.getId(), size); |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", volumeName, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); |
| } else { |
| answer = new Answer(null, false, String.format("Could not create StorPool volume %s. Error: %s", name, resp.getError())); |
| } |
| } catch (Exception e) { |
| answer = new Answer(null, false, String.format("Could not create volume due to %s", e.getMessage())); |
| } |
| } else { |
| answer = new Answer(null, false, String.format("Invalid object type \"%s\" passed to createAsync", data.getType())); |
| } |
| CreateCmdResult res = new CreateCmdResult(path, answer); |
| res.setResult(answer.getDetails()); |
| if (callback != null) { |
| callback.complete(res); |
| } |
| } |
| |
| private void updateVolume(DataStore dataStore, String path, VolumeInfo vinfo) { |
| VolumeVO volume = volumeDao.findById(vinfo.getId()); |
| volume.setPoolId(dataStore.getId()); |
| volume.setPath(path); |
| volume.setPoolType(StoragePoolType.StorPool); |
| volumeDao.update(volume.getId(), volume); |
| } |
| |
| private StorPoolSetVolumeEncryptionAnswer createEncryptedVolume(DataStore dataStore, DataObject data, VolumeInfo vinfo, Long size, VolumeObjectTO volume, String parentName, boolean isDataDisk) { |
| StorPoolSetVolumeEncryptionAnswer ans; |
| EndPoint ep = null; |
| if (parentName == null) { |
| ep = selector.select(data, vinfo.getPassphraseId() != null); |
| } else { |
| Long clusterId = StorPoolHelper.findClusterIdByGlobalId(parentName, clusterDao); |
| if (clusterId == null) { |
| ep = selector.select(data, vinfo.getPassphraseId() != null); |
| } else { |
| List<HostVO> hosts = hostDao.findByClusterIdAndEncryptionSupport(clusterId); |
| ep = CollectionUtils.isNotEmpty(hosts) ? RemoteHostEndPoint.getHypervisorHostEndPoint(hosts.get(0)) : ep; |
| } |
| } |
| if (ep == null) { |
| ans = new StorPoolSetVolumeEncryptionAnswer(null, false, "Could not find a host with volume encryption"); |
| } else { |
| StorPoolSetVolumeEncryptionCommand cmd = new StorPoolSetVolumeEncryptionCommand(volume, parentName, isDataDisk); |
| ans = (StorPoolSetVolumeEncryptionAnswer) ep.sendMessage(cmd); |
| if (ans.getResult()) { |
| updateStoragePool(dataStore.getId(), size); |
| } |
| } |
| return ans; |
| } |
| |
| @Override |
| public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { |
| String path = null; |
| String err = null; |
| ResizeVolumeAnswer answer = null; |
| |
| if (data.getType() == DataObjectType.VOLUME) { |
| VolumeObject vol = (VolumeObject)data; |
| StoragePool pool = (StoragePool)data.getDataStore(); |
| ResizeVolumePayload payload = (ResizeVolumePayload)vol.getpayload(); |
| |
| final String name = StorPoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true); |
| final long oldSize = vol.getSize(); |
| Long oldMaxIops = vol.getMaxIops(); |
| |
| try { |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| |
| long maxIops = payload.newMaxIops == null ? Long.valueOf(0) : payload.newMaxIops; |
| |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s, maxIops=%s", name, vol.getUuid(), oldSize, payload.newSize, payload.shrinkOk, maxIops); |
| |
| SpApiResponse resp = StorPoolUtil.volumeUpdate(name, payload.newSize, payload.shrinkOk, maxIops, conn); |
| if (resp.getError() != null) { |
| err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError()); |
| } else { |
| StorPoolResizeVolumeCommand resizeCmd = new StorPoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), payload.newSize, payload.shrinkOk, |
| payload.instanceName, payload.hosts == null ? false : true); |
| answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, payload.hosts, resizeCmd); |
| |
| if (answer == null || !answer.getResult()) { |
| err = answer != null ? answer.getDetails() : "return a null answer, resize failed for unknown reason"; |
| } else { |
| path = StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false)); |
| |
| vol.setSize(payload.newSize); |
| vol.update(); |
| if (payload.newMaxIops != null) { |
| VolumeVO volume = volumeDao.findById(vol.getId()); |
| volume.setMaxIops(payload.newMaxIops); |
| volumeDao.update(volume.getId(), volume); |
| } |
| |
| updateStoragePool(vol.getPoolId(), payload.newSize - oldSize); |
| } |
| } |
| if (err != null) { |
| // try restoring volume to its initial size |
| resp = StorPoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); |
| if (resp.getError() != null) { |
| log.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, resp.getError())); |
| } |
| } |
| } catch (Exception e) { |
| log.debug("sending resize command failed", e); |
| err = e.toString(); |
| } |
| } else { |
| err = String.format("Invalid object type \"%s\" passed to resize", data.getType()); |
| } |
| |
| CreateCmdResult res = new CreateCmdResult(path, answer); |
| res.setResult(err); |
| callback.complete(res); |
| } |
| |
| @Override |
| public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CommandResult> callback) { |
| String err = null; |
| if (data.getType() == DataObjectType.VOLUME) { |
| VolumeInfo vinfo = (VolumeInfo)data; |
| String name = StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.deleteAsync delete volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s dataStore=%s", name, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), dataStore.getUuid()); |
| if (name == null) { |
| name = vinfo.getUuid(); |
| } |
| try { |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); |
| |
| SpApiResponse resp = StorPoolUtil.volumeDelete(name, conn); |
| if (resp.getError() == null) { |
| updateStoragePool(dataStore.getId(), - vinfo.getSize()); |
| VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorPoolUtil.SP_PROVIDER_NAME); |
| if (detail != null) { |
| volumeDetailsDao.remove(detail.getId()); |
| } |
| } else { |
| if (!resp.getError().getName().equalsIgnoreCase("objectDoesNotExist")) { |
| err = String.format("Could not delete StorPool volume %s. Error: %s", name, resp.getError()); |
| } |
| } |
| } catch (Exception e) { |
| err = String.format("Could not delete volume due to %s", e.getMessage()); |
| } |
| } else { |
| err = String.format("Invalid DataObjectType \"%s\" passed to deleteAsync", data.getType()); |
| } |
| |
| if (err != null) { |
| log.error(err); |
| StorPoolUtil.spLog(err); |
| } |
| |
| CommandResult res = new CommandResult(); |
| res.setResult(err); |
| callback.complete(res); |
| } |
| |
| private void logDataObject(final String pref, DataObject data) { |
| final DataStore dstore = data.getDataStore(); |
| String name = null; |
| Long size = null; |
| |
| if (data.getType() == DataObjectType.VOLUME) { |
| VolumeInfo vinfo = (VolumeInfo)data; |
| name = vinfo.getName(); |
| size = vinfo.getSize(); |
| } else if (data.getType() == DataObjectType.SNAPSHOT) { |
| SnapshotInfo sinfo = (SnapshotInfo)data; |
| name = sinfo.getName(); |
| size = sinfo.getSize(); |
| } else if (data.getType() == DataObjectType.TEMPLATE) { |
| TemplateInfo tinfo = (TemplateInfo)data; |
| name = tinfo.getName(); |
| size = tinfo.getSize(); |
| } |
| |
| StorPoolUtil.spLog("%s: name=%s, size=%s, uuid=%s, type=%s, dstore=%s:%s:%s", pref, name, size, data.getUuid(), data.getType(), dstore.getUuid(), dstore.getName(), dstore.getRole()); |
| } |
| |
| @Override |
| public boolean canCopy(DataObject srcData, DataObject dstData) { |
| return true; |
| } |
| |
| @Override |
| public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCallback<CopyCommandResult> callback) { |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc:"); |
| logDataObject("SRC", srcData); |
| logDataObject("DST", dstData); |
| |
| final DataObjectType srcType = srcData.getType(); |
| final DataObjectType dstType = dstData.getType(); |
| String err = null; |
| Answer answer = null; |
| StorageSubSystemCommand cmd = null; |
| |
| try { |
| if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.VOLUME) { |
| SnapshotInfo sinfo = (SnapshotInfo)srcData; |
| final String snapshotName = StorPoolHelper.getSnapshotName(srcData.getId(), srcData.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); |
| |
| VolumeInfo vinfo = (VolumeInfo)dstData; |
| final String volumeName = vinfo.getUuid(); |
| final Long size = vinfo.getSize(); |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| SpApiResponse resp = StorPoolUtil.volumeCreate(volumeName, snapshotName, size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); |
| if (resp.getError() == null) { |
| updateStoragePool(dstData.getDataStore().getId(), size); |
| |
| VolumeObjectTO to = (VolumeObjectTO)dstData.getTO(); |
| to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); |
| to.setSize(size); |
| |
| answer = new CopyCmdAnswer(to); |
| StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorPoolUtil.getNameFromResponse(resp, false), to.getUuid(), snapshotName, sinfo.getUuid()); |
| } else if (resp.getError().getName().equals("objectDoesNotExist")) { |
| //check if snapshot is on secondary storage |
| StorPoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snopshot on secondary storage", snapshotName); |
| SnapshotDataStoreVO snap = getSnapshotImageStoreRef(sinfo.getId(), vinfo.getDataCenterId()); |
| if (snap != null && StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getInstallPath(), false) == null) { |
| resp = StorPoolUtil.volumeCreate(srcData.getUuid(), null, size, null, "no", "snapshot", sinfo.getBaseVolume().getMaxIops(), conn); |
| if (resp.getError() == null) { |
| VolumeObjectTO dstTO = (VolumeObjectTO) dstData.getTO(); |
| dstTO.setSize(size); |
| dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); |
| cmd = new StorPoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); |
| |
| EndPoint ep = selector.select(srcData, dstData); |
| if (ep == null) { |
| err = "No remote endpoint to send command, check if host or ssvm is down?"; |
| } else { |
| answer = ep.sendMessage(cmd); |
| } |
| |
| if (answer != null && answer.getResult()) { |
| SpApiResponse resp2 = StorPoolUtil.volumeFreeze(StorPoolUtil.getNameFromResponse(resp, true), conn); |
| if (resp2.getError() != null) { |
| err = String.format("Could not freeze Storpool volume %s. Error: %s", srcData.getUuid(), resp2.getError()); |
| } else { |
| String name = StorPoolUtil.getNameFromResponse(resp, false); |
| SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(sinfo.getId(), sinfo.getUuid()); |
| if (snapshotDetails != null) { |
| StorPoolHelper.updateSnapshotDetailsValue(snapshotDetails.getId(), StorPoolUtil.devPath(name), "snapshot"); |
| }else { |
| StorPoolHelper.addSnapshotDetails(sinfo.getId(), sinfo.getUuid(), StorPoolUtil.devPath(name), _snapshotDetailsDao); |
| } |
| resp = StorPoolUtil.volumeCreate(volumeName, StorPoolUtil.getNameFromResponse(resp, true), size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); |
| if (resp.getError() == null) { |
| updateStoragePool(dstData.getDataStore().getId(), size); |
| |
| VolumeObjectTO to = (VolumeObjectTO) dstData.getTO(); |
| to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); |
| to.setSize(size); |
| // successfully downloaded snapshot to primary storage |
| answer = new CopyCmdAnswer(to); |
| StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", name, to.getUuid(), snapshotName, sinfo.getUuid()); |
| |
| } else { |
| err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); |
| } |
| } |
| } else { |
| err = answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned."; |
| } |
| } else { |
| err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); |
| } |
| } else { |
| err = String.format("The snapshot %s does not exists neither on primary, neither on secondary storage. Cannot create volume from snapshot", snapshotName); |
| } |
| } else { |
| err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); |
| } |
| } else if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.SNAPSHOT) { |
| // bypass secondary storage |
| if (StorPoolConfigurationManager.BypassSecondaryStorage.value()) { |
| SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData.getTO(); |
| answer = new CopyCmdAnswer(snapshot); |
| } else { |
| // copy snapshot to secondary storage (backup snapshot) |
| cmd = new StorPoolBackupSnapshotCommand(srcData.getTO(), dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.BackupSnapshotWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); |
| |
| final String snapName = StorPoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) srcData).getPath(), true); |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| try { |
| Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapName, clusterDao); |
| EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); |
| if (ep == null) { |
| err = "No remote endpoint to send command, check if host or ssvm is down?"; |
| } else { |
| answer = ep.sendMessage(cmd); |
| // if error during snapshot backup, cleanup the StorPool snapshot |
| if (answer != null && !answer.getResult()) { |
| StorPoolUtil.spLog(String.format("Error while backing-up snapshot '%s' - cleaning up StorPool snapshot. Error: %s", snapName, answer.getDetails())); |
| SpApiResponse resp = StorPoolUtil.snapshotDelete(snapName, conn); |
| if (resp.getError() != null) { |
| final String err2 = String.format("Failed to cleanup StorPool snapshot '%s'. Error: %s.", snapName, resp.getError()); |
| log.error(err2); |
| StorPoolUtil.spLog(err2); |
| } |
| } |
| } |
| } catch (CloudRuntimeException e) { |
| err = e.getMessage(); |
| } |
| } |
| } else if (srcType == DataObjectType.VOLUME && dstType == DataObjectType.TEMPLATE) { |
| // create template from volume |
| VolumeObjectTO volume = (VolumeObjectTO) srcData.getTO(); |
| TemplateObjectTO template = (TemplateObjectTO) dstData.getTO(); |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| |
| String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); |
| |
| |
| cmd = new StorPoolBackupTemplateFromSnapshotCommand(volume, template, |
| StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); |
| |
| try { |
| Long clusterId = StorPoolHelper.findClusterIdByGlobalId(volumeName, clusterDao); |
| EndPoint ep2 = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); |
| if (ep2 == null) { |
| err = "No remote endpoint to send command, check if host or ssvm is down?"; |
| } else { |
| answer = ep2.sendMessage(cmd); |
| if (answer != null && answer.getResult()) { |
| SpApiResponse resSnapshot = StorPoolUtil.volumeSnapshot(volumeName, template.getUuid(), null, "template", "no", conn); |
| if (resSnapshot.getError() != null) { |
| log.debug(String.format("Could not snapshot volume with ID=%s", volume.getId())); |
| StorPoolUtil.spLog("Volume snapshot failed with error=%s", resSnapshot.getError().getDescr()); |
| err = resSnapshot.getError().getDescr(); |
| } |
| else { |
| StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(), StorPoolUtil.devPath(StorPoolUtil.getSnapshotNameFromResponse(resSnapshot, false, StorPoolUtil.GLOBAL_ID)), vmTemplateDataStoreDao); |
| vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(srcData.getDataStore().getId()), false)); |
| } |
| }else { |
| err = "Could not copy template to secondary " + answer.getResult(); |
| } |
| } |
| }catch (CloudRuntimeException e) { |
| err = e.getMessage(); |
| } |
| } else if (srcType == DataObjectType.TEMPLATE && dstType == DataObjectType.TEMPLATE) { |
| // copy template to primary storage |
| TemplateInfo tinfo = (TemplateInfo)dstData; |
| Long size = tinfo.getSize(); |
| if(size == null || size == 0) |
| size = 1L*1024*1024*1024; |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| |
| TemplateDataStoreVO templDataStoreVO = vmTemplateDataStoreDao.findByTemplate(tinfo.getId(), DataStoreRole.Image); |
| |
| String snapshotName = (templDataStoreVO != null && templDataStoreVO.getLocalDownloadPath() != null) |
| ? StorPoolStorageAdaptor.getVolumeNameFromPath(templDataStoreVO.getLocalDownloadPath(), true) |
| : null; |
| String name = tinfo.getUuid(); |
| |
| SpApiResponse resp = null; |
| if (snapshotName != null) { |
| //no need to copy volume from secondary, because we have it already on primary. Just need to create a child snapshot from it. |
| //The child snapshot is needed when configuration "storage.cleanup.enabled" is true, not to clean the base snapshot and to lose everything |
| resp = StorPoolUtil.volumeCreate(name, snapshotName, size, null, "no", "template", null, conn); |
| if (resp.getError() != null) { |
| err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); |
| } else { |
| String volumeNameToSnapshot = StorPoolUtil.getNameFromResponse(resp, true); |
| SpApiResponse resp2 = StorPoolUtil.volumeFreeze(volumeNameToSnapshot, conn); |
| if (resp2.getError() != null) { |
| err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); |
| } else { |
| StorPoolUtil.spLog("Storpool snapshot [%s] for a template exists. Creating template on Storpool with name [%s]", tinfo.getUuid(), name); |
| TemplateObjectTO dstTO = (TemplateObjectTO) dstData.getTO(); |
| dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); |
| dstTO.setSize(size); |
| answer = new CopyCmdAnswer(dstTO); |
| } |
| } |
| } else { |
| resp = StorPoolUtil.volumeCreate(name, null, size, null, "no", "template", null, conn); |
| if (resp.getError() != null) { |
| err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); |
| } else { |
| TemplateObjectTO dstTO = (TemplateObjectTO)dstData.getTO(); |
| dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); |
| dstTO.setSize(size); |
| |
| cmd = new StorPoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); |
| |
| EndPoint ep = selector.select(srcData, dstData); |
| if (ep == null) { |
| err = "No remote endpoint to send command, check if host or ssvm is down?"; |
| } else { |
| answer = ep.sendMessage(cmd); |
| } |
| |
| if (answer != null && answer.getResult()) { |
| // successfully downloaded template to primary storage |
| SpApiResponse resp2 = StorPoolUtil.volumeFreeze(StorPoolUtil.getNameFromResponse(resp, true), conn); |
| if (resp2.getError() != null) { |
| err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); |
| } |
| } else { |
| err = answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned."; |
| } |
| } |
| } |
| if (err != null) { |
| resp = StorPoolUtil.volumeDelete(StorPoolUtil.getNameFromResponse(resp, true), conn); |
| if (resp.getError() != null) { |
| log.warn(String.format("Could not clean-up Storpool volume %s. Error: %s", name, resp.getError())); |
| } |
| } |
| } else if (srcType == DataObjectType.TEMPLATE && dstType == DataObjectType.VOLUME) { |
| // create volume from template on Storpool PRIMARY |
| TemplateInfo tinfo = (TemplateInfo)srcData; |
| |
| VolumeInfo vinfo = (VolumeInfo) dstData; |
| VMTemplateStoragePoolVO templStoragePoolVO = StorPoolHelper.findByPoolTemplate(vinfo.getPoolId(), |
| tinfo.getId()); |
| final String parentName = templStoragePoolVO.getLocalDownloadPath() != null |
| ? StorPoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getLocalDownloadPath(), true) |
| : StorPoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getInstallPath(), true); |
| final String name = vinfo.getUuid(); |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), |
| vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| |
| Long snapshotSize = templStoragePoolVO.getTemplateSize(); |
| boolean withoutEncryption = vinfo.getPassphraseId() == null; |
| long size = withoutEncryption ? vinfo.getSize() : vinfo.getSize() + 2097152; |
| if (snapshotSize != null && size < snapshotSize) { |
| StorPoolUtil.spLog(String.format("provided size is too small for snapshot. Provided %d, snapshot %d. Using snapshot size", size, snapshotSize)); |
| size = withoutEncryption ? snapshotSize : snapshotSize + 2097152; |
| } |
| StorPoolUtil.spLog(String.format("volume size is: %d", size)); |
| Long vmId = vinfo.getInstanceId(); |
| SpApiResponse resp = StorPoolUtil.volumeCreate(name, parentName, size, getVMInstanceUUID(vmId), getVcPolicyTag(vmId), |
| "volume", vinfo.getMaxIops(), conn); |
| if (resp.getError() == null) { |
| updateStoragePool(dstData.getDataStore().getId(), vinfo.getSize()); |
| updateVolumePoolType(vinfo); |
| |
| if (withoutEncryption) { |
| VolumeObjectTO to = updateVolumeObjectTO(vinfo, resp); |
| answer = new CopyCmdAnswer(to); |
| } else { |
| VolumeObjectTO volume = updateVolumeObjectTO(vinfo, resp); |
| String snapshotPath = StorPoolUtil.devPath(parentName.split("~")[1]); |
| answer = createEncryptedVolume(dstData.getDataStore(), dstData, vinfo, size, volume, snapshotPath, false); |
| if (answer.getResult()) { |
| answer = new CopyCmdAnswer(((StorPoolSetVolumeEncryptionAnswer) answer).getVolume()); |
| } |
| } |
| } else { |
| err = String.format("Could not create Storpool volume %s. Error: %s", name, resp.getError()); |
| } |
| } else if (srcType == DataObjectType.VOLUME && dstType == DataObjectType.VOLUME) { |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.copyAsync src Data Store=%s", srcData.getDataStore().getDriver()); |
| VolumeInfo dstInfo = (VolumeInfo)dstData; |
| VolumeInfo srcInfo = (VolumeInfo) srcData; |
| |
| if( !(srcData.getDataStore().getDriver() instanceof StorPoolPrimaryDataStoreDriver ) ) { |
| // copy "VOLUME" to primary storage |
| String name = dstInfo.getUuid(); |
| Long size = dstInfo.getSize(); |
| if(size == null || size == 0) |
| size = 1L*1024*1024*1024; |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| Long vmId = srcInfo.getInstanceId(); |
| |
| SpApiResponse resp = StorPoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vmId), getVcPolicyTag(vmId), "volume", dstInfo.getMaxIops(), conn); |
| if (resp.getError() != null) { |
| err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); |
| } else { |
| //updateVolume(dstData.getId()); |
| VolumeObjectTO dstTO = (VolumeObjectTO)dstData.getTO(); |
| dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); |
| dstTO.setSize(size); |
| |
| cmd = new StorPoolDownloadVolumeCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); |
| |
| EndPoint ep = selector.select(srcData, dstData); |
| |
| if (ep == null || storagePoolHostDao.findByPoolHost(dstData.getId(), ep.getId()) == null) { |
| StorPoolUtil.spLog("select(srcData, dstData) returned NULL or the destination pool is not connected to the selected host. Trying dstData"); |
| ep = selector.select(dstData); // Storpool is zone |
| } |
| if (ep == null) { |
| err = "No remote endpoint to send command, check if host or ssvm is down?"; |
| } else { |
| StorPoolUtil.spLog("Sending command to %s", ep.getHostAddr()); |
| answer = ep.sendMessage(cmd); |
| |
| if (answer != null && answer.getResult()) { |
| // successfully downloaded volume to primary storage |
| } else { |
| err = answer != null ? answer.getDetails() : "Unknown error while downloading volume. Null answer returned."; |
| } |
| } |
| |
| if (err != null) { |
| SpApiResponse resp3 = StorPoolUtil.volumeDelete(name, conn); |
| if (resp3.getError() != null) { |
| log.warn(String.format("Could not clean-up Storpool volume %s. Error: %s", name, resp3.getError())); |
| } |
| } |
| } |
| } else { |
| // download volume - first copies to secondary |
| VolumeObjectTO srcTO = (VolumeObjectTO)srcData.getTO(); |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc SRC path=%s DST canonicalName=%s ", srcTO.getPath(), dstData.getDataStore().getClass().getCanonicalName()); |
| PrimaryDataStoreTO checkStoragePool = dstData.getTO().getDataStore() instanceof PrimaryDataStoreTO ? (PrimaryDataStoreTO)dstData.getTO().getDataStore() : null; |
| final String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); |
| |
| if (checkStoragePool != null && checkStoragePool.getPoolType().equals(StoragePoolType.StorPool)) { |
| answer = migrateVolumeToStorPool(srcData, dstData, srcInfo, srcTO, volumeName); |
| } else { |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| final SpApiResponse resp = StorPoolUtil.volumeSnapshot(volumeName, srcTO.getUuid(), srcInfo.getInstanceId() != null ? getVMInstanceUUID(srcInfo.getInstanceId()) : null, "temporary", null, conn); |
| String snapshotName = StorPoolUtil.getSnapshotNameFromResponse(resp, true, StorPoolUtil.GLOBAL_ID); |
| if (resp.getError() == null) { |
| srcTO.setPath(StorPoolUtil.devPath( |
| StorPoolUtil.getSnapshotNameFromResponse(resp, false, StorPoolUtil.GLOBAL_ID))); |
| |
| cmd = new StorPoolCopyVolumeToSecondaryCommand(srcTO, dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.CopyVolumeWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); |
| |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc command=%s ", cmd); |
| |
| try { |
| Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapshotName, clusterDao); |
| EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); |
| StorPoolUtil.spLog("selector.select(srcData, dstData) ", ep); |
| if (ep == null) { |
| ep = selector.select(dstData); |
| StorPoolUtil.spLog("selector.select(srcData) ", ep); |
| } |
| |
| if (ep == null) { |
| err = "No remote endpoint to send command, check if host or ssvm is down?"; |
| } else { |
| answer = ep.sendMessage(cmd); |
| StorPoolUtil.spLog("Answer: details=%s, result=%s", answer.getDetails(), answer.getResult()); |
| } |
| } catch (CloudRuntimeException e) { |
| err = e.getMessage(); |
| } |
| } else { |
| err = String.format("Failed to create temporary StorPool snapshot while trying to download volume %s (uuid %s). Error: %s", srcTO.getName(), srcTO.getUuid(), resp.getError()); |
| } |
| final SpApiResponse resp2 = StorPoolUtil.snapshotDelete(snapshotName, conn); |
| if (resp2.getError() != null) { |
| final String err2 = String.format("Failed to delete temporary StorPool snapshot %s. Error: %s", StorPoolUtil.getNameFromResponse(resp, true), resp2.getError()); |
| log.error(err2); |
| StorPoolUtil.spLog(err2); |
| } |
| } |
| } |
| } else { |
| err = String.format("Unsupported copy operation from %s (type %s) to %s (type %s)", srcData.getUuid(), srcType, dstData.getUuid(), dstType); |
| } |
| } catch (Exception e) { |
| StorPoolUtil.spLog("Caught exception: %s", e.toString()); |
| err = e.toString(); |
| } |
| |
| if (answer != null && !answer.getResult()) { |
| err = answer.getDetails(); |
| } |
| |
| if (err != null) { |
| StorPoolUtil.spLog("Failed due to %s", err); |
| |
| log.error(err); |
| answer = new Answer(cmd, false, err); |
| } |
| |
| CopyCommandResult res = new CopyCommandResult(null, answer); |
| res.setResult(err); |
| callback.complete(res); |
| } |
| |
| private void updateVolumePoolType(VolumeInfo vinfo) { |
| VolumeVO volumeVO = volumeDao.findById(vinfo.getId()); |
| volumeVO.setPoolType(StoragePoolType.StorPool); |
| volumeDao.update(volumeVO.getId(), volumeVO); |
| } |
| |
| private VolumeObjectTO updateVolumeObjectTO(VolumeInfo vinfo, SpApiResponse resp) { |
| VolumeObjectTO to = (VolumeObjectTO) vinfo.getTO(); |
| to.setSize(vinfo.getSize()); |
| to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); |
| return to; |
| } |
| |
| /** |
| * Live migrate/copy volume from one StorPool storage to another |
| * @param srcData The source volume data |
| * @param dstData The destination volume data |
| * @param srcInfo The source volume info |
| * @param srcTO The source Volume TO |
| * @param volumeName The name of the volume |
| * @return Answer |
| */ |
| private Answer migrateVolumeToStorPool(DataObject srcData, DataObject dstData, VolumeInfo srcInfo, |
| VolumeObjectTO srcTO, final String volumeName) { |
| Answer answer; |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| String baseOn = StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); |
| |
| String vmUuid = null; |
| String vcPolicyTag = null; |
| |
| VMInstanceVO vm = null; |
| if (srcInfo.getInstanceId() != null) { |
| vm = vmInstanceDao.findById(srcInfo.getInstanceId()); |
| } |
| |
| if (vm != null) { |
| vmUuid = vm.getUuid(); |
| vcPolicyTag = getVcPolicyTag(vm.getId()); |
| } |
| |
| if (vm != null && vm.getState().equals(State.Running)) { |
| answer = migrateVolume(srcData, dstData, volumeName, conn); |
| } else { |
| answer = copyVolume(srcInfo, srcTO, conn, baseOn, vmUuid, vcPolicyTag); |
| } |
| return answer; |
| } |
| |
| /** |
| * Copy the volume from StorPool primary storage to another StorPool primary storage |
| * @param srcInfo The source volume info |
| * @param srcTO The source Volume TO |
| * @param conn StorPool connection |
| * @param baseOn The name of an already existing volume that the new volume is to be a copy of. |
| * @param vmUuid The UUID of the VM |
| * @param vcPolicyTag The VC policy tag |
| * @return Answer |
| */ |
| private Answer copyVolume(VolumeInfo srcInfo, VolumeObjectTO srcTO, SpConnectionDesc conn, String baseOn, String vmUuid, String vcPolicyTag) { |
| //uuid tag will be the same as srcData.uuid |
| String volumeName = srcInfo.getUuid(); |
| Long iops = (srcInfo.getMaxIops() != null && srcInfo.getMaxIops().longValue() > 0) ? srcInfo.getMaxIops() : null; |
| SpApiResponse response = StorPoolUtil.volumeCopy(volumeName, baseOn, "volume", iops, vmUuid, vcPolicyTag, conn); |
| if (response.getError() != null) { |
| return new CopyCmdAnswer(String.format("Could not copy volume [%s] due to %s", baseOn, response.getError())); |
| } |
| String newVolume = StorPoolUtil.getNameFromResponse(response, false); |
| |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc copy volume[%s] from pool[%s] with a new name [%s]", |
| baseOn, srcInfo.getDataStore().getName(), newVolume); |
| |
| srcTO.setSize(srcInfo.getSize()); |
| srcTO.setPath(StorPoolUtil.devPath(newVolume)); |
| |
| return new CopyCmdAnswer(srcTO); |
| } |
| |
| /** |
| * Live migrate the StorPool's volume to another StorPool template |
| * @param srcData The source data volume |
| * @param dstData The destination data volume |
| * @param name The volume's name |
| * @param conn StorPool's connection |
| * @return Answer |
| */ |
| private Answer migrateVolume(DataObject srcData, DataObject dstData, String name, SpConnectionDesc conn) { |
| Answer answer; |
| SpApiResponse resp = StorPoolUtil.volumeUpdateTemplate(name, conn); |
| if (resp.getError() != null) { |
| answer = new Answer(null, false, String.format("Could not migrate volume %s to %s due to %s", name, conn.getTemplateName(), resp.getError())); |
| } else { |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc migrate volume[%s] from pool[%s] to pool[%s]", |
| name, srcData.getDataStore().getName(),dstData.getDataStore().getName()); |
| VolumeVO updatedVolume = volumeDao.findById(srcData.getId()); |
| updatedVolume.setPoolId(dstData.getDataStore().getId()); |
| updatedVolume.setLastPoolId(srcData.getDataStore().getId()); |
| volumeDao.update(updatedVolume.getId(), updatedVolume); |
| answer = new Answer(null, true, null); |
| } |
| return answer; |
| } |
| |
| @Override |
| public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { |
| String snapshotName = snapshot.getUuid(); |
| VolumeInfo vinfo = snapshot.getBaseVolume(); |
| String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); |
| Long vmId = vinfo.getInstanceId(); |
| if (volumeName != null) { |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.takeSnapshot volumename=%s vmInstance=%s",volumeName, vmId); |
| } else { |
| throw new UnsupportedOperationException("The path should be: " + StorPoolUtil.SP_DEV_PATH); |
| } |
| |
| CreateObjectAnswer answer = null; |
| String err = null; |
| |
| try { |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| |
| SpApiResponse resp = StorPoolUtil.volumeSnapshot(volumeName, snapshotName, vmId != null ? getVMInstanceUUID(vmId) : null, "snapshot", null, conn); |
| |
| if (resp.getError() != null) { |
| err = String.format("Could not snapshot StorPool volume %s. Error %s", volumeName, resp.getError()); |
| answer = new CreateObjectAnswer(err); |
| } else { |
| String name = StorPoolUtil.getSnapshotNameFromResponse(resp, true, StorPoolUtil.GLOBAL_ID); |
| SnapshotObjectTO snapTo = (SnapshotObjectTO)snapshot.getTO(); |
| snapTo.setPath(StorPoolUtil.devPath(name.split("~")[1])); |
| answer = new CreateObjectAnswer(snapTo); |
| StorPoolHelper.addSnapshotDetails(snapshot.getId(), snapshot.getUuid(), snapTo.getPath(), _snapshotDetailsDao); |
| //add primary storage of snapshot |
| StorPoolHelper.addSnapshotDetails(snapshot.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(snapshot.getDataStore().getId()), _snapshotDetailsDao); |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.takeSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", name, snapshot.getUuid(), volumeName, vinfo.getUuid()); |
| } |
| } catch (Exception e) { |
| err = String.format("Could not take volume snapshot due to %s", e.getMessage()); |
| } |
| |
| CreateCmdResult res = new CreateCmdResult(null, answer); |
| res.setResult(err); |
| callback.complete(res); |
| } |
| |
| @Override |
| public void revertSnapshot(final SnapshotInfo snapshot, final SnapshotInfo snapshotOnPrimaryStore, final AsyncCompletionCallback<CommandResult> callback) { |
| final VolumeInfo vinfo = snapshot.getBaseVolume(); |
| final String snapshotName = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); |
| final String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); |
| StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.revertSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", snapshotName, snapshot.getUuid(), volumeName, vinfo.getUuid()); |
| String err = null; |
| |
| SpConnectionDesc conn = null; |
| try { |
| conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); |
| } catch (Exception e) { |
| err = String.format("Could not revert volume due to %s", e.getMessage()); |
| completeResponse(err, callback); |
| return; |
| } |
| |
| VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorPoolUtil.SP_PROVIDER_NAME); |
| if (detail != null) { |
| //Rename volume to its global id only if it was migrated from UUID to global id |
| SpApiResponse updateVolumeResponse = StorPoolUtil.volumeUpdateRename(StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), "", StorPoolStorageAdaptor.getVolumeNameFromPath(detail.getValue(), false), conn); |
| |
| if (updateVolumeResponse.getError() != null) { |
| StorPoolUtil.spLog("Could not update StorPool's volume %s to it's globalId due to %s", StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); |
| err = String.format("Could not update StorPool's volume %s to it's globalId due to %s", StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); |
| completeResponse(err, callback); |
| return; |
| } |
| volumeDetailsDao.remove(detail.getId()); |
| } |
| |
| SpApiResponse resp = StorPoolUtil.detachAllForced(volumeName, false, conn); |
| if (resp.getError() != null) { |
| err = String.format("Could not detach StorPool volume %s due to %s", volumeName, resp.getError()); |
| completeResponse(err, callback); |
| return; |
| } |
| SpApiResponse response = StorPoolUtil.volumeRevert(volumeName, snapshotName, conn); |
| if (response.getError() != null) { |
| err = String.format( |
| "Could not revert StorPool volume %s to the %s snapshot: could not create the new volume: error %s", |
| volumeName, snapshotName, response.getError()); |
| completeResponse(err, callback); |
| return; |
| } |
| |
| if (vinfo.getMaxIops() != null) { |
| response = StorPoolUtil.volumeUpdateIopsAndTags(volumeName, null, vinfo.getMaxIops(), conn, null); |
| if (response.getError() != null) { |
| StorPoolUtil.spLog("Volume was reverted successfully but max iops could not be set due to %s", response.getError().getDescr()); |
| } |
| } |
| |
| final VolumeObjectTO to = (VolumeObjectTO)vinfo.getTO(); |
| completeResponse(to, callback); |
| } |
| |
| private String getVcPolicyTag(Long vmId) { |
| ResourceTag resourceTag = vmId != null ? _resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorPoolUtil.SP_VC_POLICY) : null; |
| return resourceTag != null ? resourceTag.getValue() : ""; |
| } |
| |
| public void handleQualityOfServiceForVolumeMigration(VolumeInfo arg0, QualityOfServiceState arg1) { |
| log.debug(String.format("handleQualityOfServiceForVolumeMigration with volume name=%s is not supported", arg0.getName())); |
| } |
| |
| |
| public void copyAsync(DataObject srcData, DataObject destData, Host destHost, |
| AsyncCompletionCallback<CopyCommandResult> callback) { |
| copyAsync(srcData, destData, callback); |
| } |
| |
| public boolean canProvideStorageStats() { |
| return StorPoolConfigurationManager.StorageStatsInterval.value() > 0; |
| } |
| |
| public Pair<Long, Long> getStorageStats(StoragePool storagePool) { |
| if (storagePool == null) { |
| return null; |
| } |
| Map<Long, Map<String, Pair<Long, Long>>> templatesStats = StorPoolStatsCollector.templatesStats; |
| if (MapUtils.isNotEmpty(templatesStats) && templatesStats.containsKey(storagePool.getDataCenterId())) { |
| Map<String, Pair<Long, Long>> storageStats = templatesStats.get(storagePool.getDataCenterId()); |
| StoragePoolDetailVO templateName = storagePoolDetailsDao.findDetail(storagePool.getId(), StorPoolUtil.SP_TEMPLATE); |
| if (storageStats.containsKey(templateName.getValue()) && templateName != null) { |
| Pair<Long, Long> stats = storageStats.get(templateName.getValue()); |
| if (stats.first() != storagePool.getCapacityBytes()) { |
| primaryStoreDao.updateCapacityBytes(storagePool.getId(), stats.first()); |
| } |
| return storageStats.get(templateName.getValue()); |
| } |
| } |
| return null; |
| } |
| |
| public boolean canProvideVolumeStats() { |
| return StorPoolConfigurationManager.VolumesStatsInterval.value() > 0; |
| } |
| |
| public Pair<Long, Long> getVolumeStats(StoragePool storagePool, String volumeId) { |
| |
| if (volumeId == null) { |
| return null; |
| } |
| |
| Map<String, Pair<Long, Long>> volumesStats = StorPoolStatsCollector.volumesStats; |
| if (MapUtils.isNotEmpty(volumesStats)) { |
| Pair<Long, Long> volumeStats = volumesStats.get(StorPoolStorageAdaptor.getVolumeNameFromPath(volumeId, true)); |
| if (volumeStats != null) { |
| return volumeStats; |
| } |
| } else { |
| List<VolumeVO> volumes = volumeDao.findByPoolId(storagePool.getId()); |
| for (VolumeVO volume : volumes) { |
| if (volume.getPath() != null && volume.getPath().equals(volumeId)) { |
| long size = volume.getSize(); |
| StorPoolUtil.spLog("Volume [%s] doesn't have any statistics, returning its size [%s]", volumeId, size); |
| return new Pair<>(size, size); |
| } |
| } |
| } |
| return null; |
| } |
| |
| public boolean canHostAccessStoragePool(Host host, StoragePool pool) { |
| return false; |
| } |
| |
| @Override |
| public boolean isVmInfoNeeded() { |
| return true; |
| } |
| |
| @Override |
| public void provideVmInfo(long vmId, long volumeId) { |
| VolumeVO volume = volumeDao.findById(volumeId); |
| if (volume.getInstanceId() == null) { |
| return; |
| } |
| StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); |
| if (poolVO != null) { |
| try { |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); |
| String volName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); |
| VMInstanceVO userVM = vmInstanceDao.findById(vmId); |
| SpApiResponse resp = StorPoolUtil.volumeUpdateIopsAndTags(volName, volume.getInstanceId() != null ? userVM.getUuid() : "", null, conn, getVcPolicyTag(vmId)); |
| if (resp.getError() != null) { |
| log.warn(String.format("Could not update VC policy tags of a volume with id [%s]", volume.getUuid())); |
| } |
| } catch (Exception e) { |
| log.warn(String.format("Could not update Virtual machine tags due to %s", e.getMessage())); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isVmTagsNeeded(String tagKey) { |
| return tagKey != null && tagKey.equals(StorPoolUtil.SP_VC_POLICY); |
| } |
| |
| @Override |
| public void provideVmTags(long vmId, long volumeId, String tagValue) { |
| VolumeVO volume = volumeDao.findById(volumeId); |
| StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); |
| if (poolVO != null) { |
| try { |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); |
| String volName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); |
| SpApiResponse resp = StorPoolUtil.volumeUpdateVCTags(volName, conn, getVcPolicyTag(vmId)); |
| if (resp.getError() != null) { |
| log.warn(String.format("Could not update VC policy tags of a volume with id [%s]", volume.getUuid())); |
| } |
| } catch (Exception e) { |
| log.warn(String.format("Could not update Virtual machine tags due to %s", e.getMessage())); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isStorageSupportHA(StoragePoolType type) { |
| return true; |
| } |
| |
| @Override |
| public void detachVolumeFromAllStorageNodes(Volume volume) { |
| StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); |
| if (poolVO != null) { |
| SpConnectionDesc conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); |
| String volName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); |
| SpApiResponse resp = StorPoolUtil.detachAllForced(volName, false, conn); |
| StorPoolUtil.spLog("The volume [%s] is detach from all clusters [%s]", volName, resp); |
| } |
| } |
| } |