// 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.volume; import java.util.Date; import javax.inject.Inject; import org.apache.log4j.Logger; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.DownloadAnswer; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering.DiskCacheMode; import com.cloud.storage.DataStoreRole; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.storage.encoding.EncodingType; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; public class VolumeObject implements VolumeInfo { private static final Logger s_logger = Logger.getLogger(VolumeObject.class); protected VolumeVO volumeVO; private StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine; protected DataStore dataStore; @Inject VolumeDao volumeDao; @Inject VolumeDataStoreDao volumeStoreDao; @Inject ObjectInDataStoreManager objectInStoreMgr; @Inject VMInstanceDao vmInstanceDao; @Inject DiskOfferingDao diskOfferingDao; private Object payload; public VolumeObject() { _volStateMachine = Volume.State.getStateMachine(); } protected void configure(DataStore dataStore, VolumeVO volumeVO) { this.volumeVO = volumeVO; this.dataStore = dataStore; } public static VolumeObject getVolumeObject(DataStore dataStore, VolumeVO volumeVO) { VolumeObject vo = ComponentContext.inject(VolumeObject.class); vo.configure(dataStore, volumeVO); return vo; } @Override public String getAttachedVmName() { Long vmId = volumeVO.getInstanceId(); if (vmId != null) { VMInstanceVO vm = vmInstanceDao.findById(vmId); if (vm == null) { return null; } return vm.getInstanceName(); } return null; } @Override public VirtualMachine getAttachedVM() { Long vmId = volumeVO.getInstanceId(); if (vmId != null) { VMInstanceVO vm = vmInstanceDao.findById(vmId); return vm; } return null; } @Override public String getUuid() { return volumeVO.getUuid(); } public void setUuid(String uuid) { volumeVO.setUuid(uuid); } @Override public String get_iScsiName() { return volumeVO.get_iScsiName(); } public void setSize(Long size) { volumeVO.setSize(size); } @Override public Volume.State getState() { return volumeVO.getState(); } @Override public DataStore getDataStore() { return dataStore; } @Override public Long getSize() { return volumeVO.getSize(); } @Override public Long getMinIops() { return volumeVO.getMinIops(); } @Override public Long getMaxIops() { return volumeVO.getMaxIops(); } @Override public Integer getHypervisorSnapshotReserve() { return volumeVO.getHypervisorSnapshotReserve(); } @Override public boolean isDisplayVolume() { return volumeVO.isDisplayVolume(); } @Override public boolean isDisplay() { return volumeVO.isDisplay(); } public long getVolumeId() { return volumeVO.getId(); } @Override public boolean stateTransit(Volume.Event event) { boolean result = false; try { volumeVO = volumeDao.findById(volumeVO.getId()); if (volumeVO != null) { result = _volStateMachine.transitTo(volumeVO, event, null, volumeDao); volumeVO = volumeDao.findById(volumeVO.getId()); } } catch (NoTransitionException e) { String errorMessage = "Failed to transit volume: " + getVolumeId() + ", due to: " + e.toString(); s_logger.debug(errorMessage); throw new CloudRuntimeException(errorMessage); } return result; } private DiskOfferingVO getDiskOfferingVO() { if (getDiskOfferingId() != null) { DiskOfferingVO diskOfferingVO = diskOfferingDao.findById(getDiskOfferingId()); return diskOfferingVO; } return null; } @Override public Long getBytesReadRate() { DiskOfferingVO diskOfferingVO = getDiskOfferingVO(); if (diskOfferingVO != null) { return diskOfferingVO.getBytesReadRate(); } return null; } @Override public Long getBytesWriteRate() { DiskOfferingVO diskOfferingVO = getDiskOfferingVO(); if (diskOfferingVO != null) { return diskOfferingVO.getBytesWriteRate(); } return null; } @Override public Long getIopsReadRate() { DiskOfferingVO diskOfferingVO = getDiskOfferingVO(); if (diskOfferingVO != null) { return diskOfferingVO.getIopsReadRate(); } return null; } @Override public Long getIopsWriteRate() { DiskOfferingVO diskOfferingVO = getDiskOfferingVO(); if (diskOfferingVO != null) { return diskOfferingVO.getIopsWriteRate(); } return null; } @Override public DiskCacheMode getCacheMode() { DiskOfferingVO diskOfferingVO = getDiskOfferingVO(); if (diskOfferingVO != null) { return diskOfferingVO.getCacheMode(); } return null; } public void update() { volumeDao.update(volumeVO.getId(), volumeVO); volumeVO = volumeDao.findById(volumeVO.getId()); } @Override public long getId() { return volumeVO.getId(); } @Override public boolean isAttachedVM() { return (volumeVO.getInstanceId() == null) ? false : true; } @Override public String getUri() { if (dataStore == null) { throw new CloudRuntimeException("datastore must be set before using this object"); } DataObjectInStore obj = objectInStoreMgr.findObject(volumeVO.getId(), DataObjectType.VOLUME, dataStore.getId(), dataStore.getRole()); if (obj.getState() != ObjectInDataStoreStateMachine.State.Ready) { return dataStore.getUri() + "&" + EncodingType.OBJTYPE + "=" + DataObjectType.VOLUME + "&" + EncodingType.SIZE + "=" + volumeVO.getSize() + "&" + EncodingType.NAME + "=" + volumeVO.getName(); } else { return dataStore.getUri() + "&" + EncodingType.OBJTYPE + "=" + DataObjectType.VOLUME + "&" + EncodingType.PATH + "=" + obj.getInstallPath(); } } @Override public DataObjectType getType() { return DataObjectType.VOLUME; } @Override public void processEvent(ObjectInDataStoreStateMachine.Event event) { if (dataStore == null) { return; } try { Volume.Event volEvent = null; if (dataStore.getRole() == DataStoreRole.ImageCache) { objectInStoreMgr.update(this, event); return; } if (dataStore.getRole() == DataStoreRole.Image) { objectInStoreMgr.update(this, event); if (volumeVO.getState() == Volume.State.Migrating || volumeVO.getState() == Volume.State.Copying || volumeVO.getState() == Volume.State.Uploaded || volumeVO.getState() == Volume.State.Expunged) { return; } if (event == ObjectInDataStoreStateMachine.Event.CreateOnlyRequested) { volEvent = Volume.Event.UploadRequested; } else if (event == ObjectInDataStoreStateMachine.Event.MigrationRequested) { volEvent = Volume.Event.CopyRequested; } } else { if (event == ObjectInDataStoreStateMachine.Event.CreateRequested || event == ObjectInDataStoreStateMachine.Event.CreateOnlyRequested) { volEvent = Volume.Event.CreateRequested; } else if (event == ObjectInDataStoreStateMachine.Event.CopyingRequested) { volEvent = Volume.Event.CopyRequested; } else if (event == ObjectInDataStoreStateMachine.Event.MigrationRequested) { volEvent = Volume.Event.MigrationRequested; } else if (event == ObjectInDataStoreStateMachine.Event.MigrationCopyRequested) { volEvent = Event.MigrationCopyRequested; } } if (event == ObjectInDataStoreStateMachine.Event.DestroyRequested) { volEvent = Volume.Event.DestroyRequested; } else if (event == ObjectInDataStoreStateMachine.Event.ExpungeRequested) { volEvent = Volume.Event.ExpungingRequested; } else if (event == ObjectInDataStoreStateMachine.Event.OperationSuccessed) { volEvent = Volume.Event.OperationSucceeded; } else if (event == ObjectInDataStoreStateMachine.Event.MigrationCopySucceeded) { volEvent = Event.MigrationCopySucceeded; } else if (event == ObjectInDataStoreStateMachine.Event.OperationFailed) { volEvent = Volume.Event.OperationFailed; } else if (event == ObjectInDataStoreStateMachine.Event.MigrationCopyFailed) { volEvent = Event.MigrationCopyFailed; } else if (event == ObjectInDataStoreStateMachine.Event.ResizeRequested) { volEvent = Volume.Event.ResizeRequested; } stateTransit(volEvent); } catch (Exception e) { s_logger.debug("Failed to update state", e); throw new CloudRuntimeException("Failed to update state:" + e.toString()); } finally { // in case of OperationFailed, expunge the entry // state transit call reloads the volume from DB and so check for null as well if (event == ObjectInDataStoreStateMachine.Event.OperationFailed && (volumeVO != null && volumeVO.getState() != Volume.State.Copying && volumeVO.getState() != Volume.State.Uploaded && volumeVO.getState() != Volume.State.UploadError)) { objectInStoreMgr.deleteIfNotReady(this); } } } @Override public void processEventOnly(ObjectInDataStoreStateMachine.Event event) { try { objectInStoreMgr.update(this, event); } catch (Exception e) { s_logger.debug("Failed to update state", e); throw new CloudRuntimeException("Failed to update state:" + e.toString()); } finally { // in case of OperationFailed, expunge the entry if (event == ObjectInDataStoreStateMachine.Event.OperationFailed) { objectInStoreMgr.deleteIfNotReady(this); } } } @Override public String getName() { return volumeVO.getName(); } @Override public Long getInstanceId() { return volumeVO.getInstanceId(); } @Override public String getFolder() { return volumeVO.getFolder(); } @Override public String getPath() { if (dataStore.getRole() == DataStoreRole.Primary) { return volumeVO.getPath(); } else { DataObjectInStore objInStore = objectInStoreMgr.findObject(this, dataStore); if (objInStore != null) { return objInStore.getInstallPath(); } else { return null; } } } @Override public Long getPodId() { return volumeVO.getPodId(); } @Override public long getDataCenterId() { return volumeVO.getDataCenterId(); } @Override public Type getVolumeType() { return volumeVO.getVolumeType(); } @Override public Long getPoolId() { return volumeVO.getPoolId(); } @Override public Date getAttached() { return volumeVO.getAttached(); } @Override public Long getDeviceId() { return volumeVO.getDeviceId(); } @Override public Date getCreated() { return volumeVO.getCreated(); } @Override public Long getDiskOfferingId() { return volumeVO.getDiskOfferingId(); } @Override public String getChainInfo() { return volumeVO.getChainInfo(); } @Override public boolean isRecreatable() { return volumeVO.isRecreatable(); } @Override public long getUpdatedCount() { return volumeVO.getUpdatedCount(); } @Override public void incrUpdatedCount() { volumeVO.incrUpdatedCount(); } @Override public Date getUpdated() { return volumeVO.getUpdated(); } @Override public String getReservationId() { return volumeVO.getReservationId(); } @Override public void setReservationId(String reserv) { volumeVO.setReservationId(reserv); } @Override public long getAccountId() { return volumeVO.getAccountId(); } @Override public long getDomainId() { return volumeVO.getDomainId(); } @Override public Long getTemplateId() { return volumeVO.getTemplateId(); } @Override public void addPayload(Object data) { payload = data; } @Override public Object getpayload() { return payload; } public VolumeVO getVolume() { return volumeVO; } @Override public HypervisorType getHypervisorType() { return volumeDao.getHypervisorType(volumeVO.getId()); } @Override public Long getLastPoolId() { return volumeVO.getLastPoolId(); } @Override public DataTO getTO() { DataTO to = getDataStore().getDriver().getTO(this); if (to == null) { to = new VolumeObjectTO(this); } return to; } @Override public void processEvent(ObjectInDataStoreStateMachine.Event event, Answer answer) { try { if (dataStore.getRole() == DataStoreRole.Primary) { if (answer instanceof CopyCmdAnswer) { CopyCmdAnswer cpyAnswer = (CopyCmdAnswer)answer; VolumeVO vol = volumeDao.findById(getId()); VolumeObjectTO newVol = (VolumeObjectTO)cpyAnswer.getNewData(); vol.setPath(newVol.getPath()); if (newVol.getSize() != null) { vol.setSize(newVol.getSize()); } if (newVol.getFormat() != null) { vol.setFormat(newVol.getFormat()); } vol.setPoolId(getDataStore().getId()); volumeDao.update(vol.getId(), vol); } else if (answer instanceof CreateObjectAnswer) { CreateObjectAnswer createAnswer = (CreateObjectAnswer)answer; VolumeObjectTO newVol = (VolumeObjectTO)createAnswer.getData(); VolumeVO vol = volumeDao.findById(getId()); vol.setPath(newVol.getPath()); if (newVol.getSize() != null) { vol.setSize(newVol.getSize()); } vol.setPoolId(getDataStore().getId()); if (newVol.getFormat() != null) { vol.setFormat(newVol.getFormat()); } volumeDao.update(vol.getId(), vol); } } else { // image store or imageCache store if (answer instanceof DownloadAnswer) { DownloadAnswer dwdAnswer = (DownloadAnswer)answer; VolumeDataStoreVO volStore = volumeStoreDao.findByStoreVolume(dataStore.getId(), getId()); volStore.setInstallPath(dwdAnswer.getInstallPath()); volStore.setChecksum(dwdAnswer.getCheckSum()); volumeStoreDao.update(volStore.getId(), volStore); } else if (answer instanceof CopyCmdAnswer) { CopyCmdAnswer cpyAnswer = (CopyCmdAnswer)answer; VolumeDataStoreVO volStore = volumeStoreDao.findByStoreVolume(dataStore.getId(), getId()); VolumeObjectTO newVol = (VolumeObjectTO)cpyAnswer.getNewData(); volStore.setInstallPath(newVol.getPath()); if (newVol.getSize() != null) { volStore.setSize(newVol.getSize()); } volumeStoreDao.update(volStore.getId(), volStore); } } } catch (RuntimeException ex) { if (event == ObjectInDataStoreStateMachine.Event.OperationFailed) { objectInStoreMgr.deleteIfNotReady(this); } throw ex; } this.processEvent(event); } @Override public void incRefCount() { if (dataStore == null) { return; } if (dataStore.getRole() == DataStoreRole.Image || dataStore.getRole() == DataStoreRole.ImageCache) { VolumeDataStoreVO store = volumeStoreDao.findByStoreVolume(dataStore.getId(), getId()); store.incrRefCnt(); store.setLastUpdated(new Date()); volumeStoreDao.update(store.getId(), store); } } @Override public void decRefCount() { if (dataStore == null) { return; } if (dataStore.getRole() == DataStoreRole.Image || dataStore.getRole() == DataStoreRole.ImageCache) { VolumeDataStoreVO store = volumeStoreDao.findByStoreVolume(dataStore.getId(), getId()); store.decrRefCnt(); store.setLastUpdated(new Date()); volumeStoreDao.update(store.getId(), store); } } @Override public Long getRefCount() { if (dataStore == null) { return null; } if (dataStore.getRole() == DataStoreRole.Image || dataStore.getRole() == DataStoreRole.ImageCache) { VolumeDataStoreVO store = volumeStoreDao.findByStoreVolume(dataStore.getId(), getId()); return store.getRefCnt(); } return null; } @Override public void processEventOnly(ObjectInDataStoreStateMachine.Event event, Answer answer) { try { if (dataStore.getRole() == DataStoreRole.Primary) { if (answer instanceof CopyCmdAnswer) { CopyCmdAnswer cpyAnswer = (CopyCmdAnswer)answer; VolumeVO vol = volumeDao.findById(getId()); VolumeObjectTO newVol = (VolumeObjectTO)cpyAnswer.getNewData(); vol.setPath(newVol.getPath()); if (newVol.getSize() != null) { vol.setSize(newVol.getSize()); } vol.setPoolId(getDataStore().getId()); volumeDao.update(vol.getId(), vol); } else if (answer instanceof CreateObjectAnswer) { CreateObjectAnswer createAnswer = (CreateObjectAnswer)answer; VolumeObjectTO newVol = (VolumeObjectTO)createAnswer.getData(); VolumeVO vol = volumeDao.findById(getId()); vol.setPath(newVol.getPath()); if (newVol.getSize() != null) { vol.setSize(newVol.getSize()); } vol.setPoolId(getDataStore().getId()); volumeDao.update(vol.getId(), vol); } } else { // image store or imageCache store if (answer instanceof DownloadAnswer) { DownloadAnswer dwdAnswer = (DownloadAnswer)answer; VolumeDataStoreVO volStore = volumeStoreDao.findByStoreVolume(dataStore.getId(), getId()); volStore.setInstallPath(dwdAnswer.getInstallPath()); volStore.setChecksum(dwdAnswer.getCheckSum()); volumeStoreDao.update(volStore.getId(), volStore); } else if (answer instanceof CopyCmdAnswer) { CopyCmdAnswer cpyAnswer = (CopyCmdAnswer)answer; VolumeDataStoreVO volStore = volumeStoreDao.findByStoreVolume(dataStore.getId(), getId()); VolumeObjectTO newVol = (VolumeObjectTO)cpyAnswer.getNewData(); volStore.setInstallPath(newVol.getPath()); if (newVol.getSize() != null) { volStore.setSize(newVol.getSize()); } volumeStoreDao.update(volStore.getId(), volStore); } } } catch (RuntimeException ex) { if (event == ObjectInDataStoreStateMachine.Event.OperationFailed) { objectInStoreMgr.deleteIfNotReady(this); } throw ex; } this.processEventOnly(event); } @Override public ImageFormat getFormat() { return volumeVO.getFormat(); } @Override public ProvisioningType getProvisioningType(){ return this.volumeVO.getProvisioningType(); } @Override public boolean delete() { if (dataStore != null) { return dataStore.delete(this); } return true; } @Override public Long getVmSnapshotChainSize() { return volumeVO.getVmSnapshotChainSize(); } @Override public Class<?> getEntityType() { return Volume.class; } }