| // 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; |
| |
| import javax.inject.Inject; |
| |
| import org.apache.log4j.Logger; |
| import org.springframework.stereotype.Component; |
| |
| import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; |
| 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.DataStoreManager; |
| import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; |
| import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; |
| import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; |
| import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; |
| import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; |
| 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.VolumeDataFactory; |
| import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; |
| import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; |
| import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; |
| import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; |
| import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; |
| import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; |
| import org.apache.cloudstack.storage.db.ObjectInDataStoreDao; |
| |
| import com.cloud.agent.api.to.DataObjectType; |
| import com.cloud.agent.api.to.S3TO; |
| import com.cloud.exception.ConcurrentOperationException; |
| import com.cloud.storage.DataStoreRole; |
| import com.cloud.storage.SnapshotVO; |
| import com.cloud.storage.VMTemplateStoragePoolVO; |
| import com.cloud.storage.dao.SnapshotDao; |
| import com.cloud.storage.dao.VMTemplateDao; |
| import com.cloud.storage.dao.VMTemplatePoolDao; |
| import com.cloud.storage.dao.VolumeDao; |
| import com.cloud.storage.template.TemplateConstants; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.utils.fsm.NoTransitionException; |
| import com.cloud.utils.fsm.StateMachine2; |
| |
| @Component |
| public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { |
| private static final Logger s_logger = Logger.getLogger(ObjectInDataStoreManagerImpl.class); |
| @Inject |
| TemplateDataFactory imageFactory; |
| @Inject |
| DataStoreManager storeMgr; |
| @Inject |
| VolumeDataFactory volumeFactory; |
| @Inject |
| TemplateDataStoreDao templateDataStoreDao; |
| @Inject |
| SnapshotDataStoreDao snapshotDataStoreDao; |
| @Inject |
| VolumeDataStoreDao volumeDataStoreDao; |
| @Inject |
| VMTemplatePoolDao templatePoolDao; |
| @Inject |
| SnapshotDataFactory snapshotFactory; |
| @Inject |
| ObjectInDataStoreDao objInStoreDao; |
| @Inject |
| VMTemplateDao templateDao; |
| @Inject |
| SnapshotDao snapshotDao; |
| @Inject |
| VolumeDao volumeDao; |
| protected StateMachine2<State, Event, DataObjectInStore> stateMachines; |
| |
| public ObjectInDataStoreManagerImpl() { |
| stateMachines = new StateMachine2<State, Event, DataObjectInStore>(); |
| stateMachines.addTransition(State.Allocated, Event.CreateOnlyRequested, State.Creating); |
| stateMachines.addTransition(State.Allocated, Event.DestroyRequested, State.Destroying); |
| stateMachines.addTransition(State.Allocated, Event.OperationFailed, State.Failed); |
| stateMachines.addTransition(State.Creating, Event.OperationFailed, State.Allocated); |
| stateMachines.addTransition(State.Creating, Event.OperationSuccessed, State.Ready); |
| stateMachines.addTransition(State.Ready, Event.CopyingRequested, State.Copying); |
| stateMachines.addTransition(State.Copying, Event.OperationSuccessed, State.Ready); |
| stateMachines.addTransition(State.Copying, Event.OperationFailed, State.Ready); |
| stateMachines.addTransition(State.Ready, Event.DestroyRequested, State.Destroying); |
| stateMachines.addTransition(State.Destroying, Event.DestroyRequested, State.Destroying); |
| stateMachines.addTransition(State.Destroying, Event.OperationSuccessed, State.Destroyed); |
| stateMachines.addTransition(State.Destroying, Event.OperationFailed, State.Destroying); |
| stateMachines.addTransition(State.Failed, Event.DestroyRequested, State.Destroying); |
| // TODO: further investigate why an extra event is sent when it is |
| // alreay Ready for DownloadListener |
| stateMachines.addTransition(State.Ready, Event.OperationSuccessed, State.Ready); |
| } |
| |
| @Override |
| public DataObject create(DataObject obj, DataStore dataStore) { |
| if (dataStore.getRole() == DataStoreRole.Primary) { |
| if (obj.getType() == DataObjectType.TEMPLATE) { |
| VMTemplateStoragePoolVO vo = new VMTemplateStoragePoolVO(dataStore.getId(), obj.getId()); |
| vo = templatePoolDao.persist(vo); |
| } else if (obj.getType() == DataObjectType.SNAPSHOT) { |
| SnapshotInfo snapshotInfo = (SnapshotInfo)obj; |
| SnapshotDataStoreVO ss = new SnapshotDataStoreVO(); |
| ss.setSnapshotId(obj.getId()); |
| ss.setDataStoreId(dataStore.getId()); |
| ss.setRole(dataStore.getRole()); |
| ss.setVolumeId(snapshotInfo.getVolumeId()); |
| ss.setSize(snapshotInfo.getSize()); // this is the virtual size of snapshot in primary storage. |
| ss.setPhysicalSize(snapshotInfo.getSize()); // this physical size will get updated with actual size once the snapshot backup is done. |
| SnapshotDataStoreVO snapshotDataStoreVO = snapshotDataStoreDao.findParent(dataStore.getRole(), dataStore.getId(), snapshotInfo.getVolumeId()); |
| if (snapshotDataStoreVO != null) { |
| //Double check the snapshot is removed or not |
| SnapshotVO parentSnap = snapshotDao.findById(snapshotDataStoreVO.getSnapshotId()); |
| if (parentSnap != null) { |
| ss.setParentSnapshotId(snapshotDataStoreVO.getSnapshotId()); |
| } else { |
| s_logger.debug("find inconsistent db for snapshot " + snapshotDataStoreVO.getSnapshotId()); |
| } |
| } |
| ss.setState(ObjectInDataStoreStateMachine.State.Allocated); |
| ss = snapshotDataStoreDao.persist(ss); |
| } |
| } else { |
| // Image store |
| switch (obj.getType()) { |
| case TEMPLATE: |
| TemplateDataStoreVO ts = new TemplateDataStoreVO(); |
| ts.setTemplateId(obj.getId()); |
| ts.setDataStoreId(dataStore.getId()); |
| ts.setDataStoreRole(dataStore.getRole()); |
| String installPath = |
| TemplateConstants.DEFAULT_TMPLT_ROOT_DIR + "/" + TemplateConstants.DEFAULT_TMPLT_FIRST_LEVEL_DIR + |
| templateDao.findById(obj.getId()).getAccountId() + "/" + obj.getId(); |
| if (dataStore.getTO() instanceof S3TO) { |
| TemplateInfo tmpl = (TemplateInfo)obj; |
| installPath += "/" + tmpl.getUniqueName(); // for S3, we |
| // append |
| // template name |
| // in the path |
| // for template |
| // sync since we |
| // don't have |
| // template.properties |
| // there |
| } |
| |
| ts.setInstallPath(installPath); |
| ts.setState(ObjectInDataStoreStateMachine.State.Allocated); |
| ts = templateDataStoreDao.persist(ts); |
| break; |
| case SNAPSHOT: |
| SnapshotInfo snapshot = (SnapshotInfo)obj; |
| SnapshotDataStoreVO ss = new SnapshotDataStoreVO(); |
| ss.setSnapshotId(obj.getId()); |
| ss.setDataStoreId(dataStore.getId()); |
| ss.setRole(dataStore.getRole()); |
| ss.setSize(snapshot.getSize()); |
| ss.setVolumeId(snapshot.getVolumeId()); |
| SnapshotDataStoreVO snapshotDataStoreVO = snapshotDataStoreDao.findParent(dataStore.getRole(), dataStore.getId(), snapshot.getVolumeId()); |
| if (snapshotDataStoreVO != null) { |
| ss.setParentSnapshotId(snapshotDataStoreVO.getSnapshotId()); |
| } |
| ss.setInstallPath(TemplateConstants.DEFAULT_SNAPSHOT_ROOT_DIR + "/" + snapshotDao.findById(obj.getId()).getAccountId() + "/" + snapshot.getVolumeId()); |
| ss.setState(ObjectInDataStoreStateMachine.State.Allocated); |
| ss = snapshotDataStoreDao.persist(ss); |
| break; |
| case VOLUME: |
| VolumeDataStoreVO vs = new VolumeDataStoreVO(); |
| vs.setVolumeId(obj.getId()); |
| vs.setDataStoreId(dataStore.getId()); |
| vs.setInstallPath(TemplateConstants.DEFAULT_VOLUME_ROOT_DIR + "/" + volumeDao.findById(obj.getId()).getAccountId() + "/" + obj.getId()); |
| vs.setState(ObjectInDataStoreStateMachine.State.Allocated); |
| vs = volumeDataStoreDao.persist(vs); |
| break; |
| } |
| } |
| |
| return this.get(obj, dataStore); |
| } |
| |
| @Override |
| public boolean delete(DataObject dataObj) { |
| long objId = dataObj.getId(); |
| DataStore dataStore = dataObj.getDataStore(); |
| if (dataStore.getRole() == DataStoreRole.Primary) { |
| if (dataObj.getType() == DataObjectType.TEMPLATE) { |
| VMTemplateStoragePoolVO destTmpltPool = templatePoolDao.findByPoolTemplate(dataStore.getId(), objId); |
| if (destTmpltPool != null) { |
| return templatePoolDao.remove(destTmpltPool.getId()); |
| } else { |
| s_logger.warn("Template " + objId + " is not found on storage pool " + dataStore.getId() + ", so no need to delete"); |
| return true; |
| } |
| } |
| } else { |
| // Image store |
| switch (dataObj.getType()) { |
| case TEMPLATE: |
| TemplateDataStoreVO destTmpltStore = templateDataStoreDao.findByStoreTemplate(dataStore.getId(), objId); |
| if (destTmpltStore != null) { |
| return templateDataStoreDao.remove(destTmpltStore.getId()); |
| } else { |
| s_logger.warn("Template " + objId + " is not found on image store " + dataStore.getId() + ", so no need to delete"); |
| return true; |
| } |
| case SNAPSHOT: |
| SnapshotDataStoreVO destSnapshotStore = snapshotDataStoreDao.findByStoreSnapshot(dataStore.getRole(), dataStore.getId(), objId); |
| if (destSnapshotStore != null) { |
| return snapshotDataStoreDao.remove(destSnapshotStore.getId()); |
| } else { |
| s_logger.warn("Snapshot " + objId + " is not found on image store " + dataStore.getId() + ", so no need to delete"); |
| return true; |
| } |
| case VOLUME: |
| VolumeDataStoreVO destVolumeStore = volumeDataStoreDao.findByStoreVolume(dataStore.getId(), objId); |
| if (destVolumeStore != null) { |
| return volumeDataStoreDao.remove(destVolumeStore.getId()); |
| } else { |
| s_logger.warn("Volume " + objId + " is not found on image store " + dataStore.getId() + ", so no need to delete"); |
| return true; |
| } |
| } |
| } |
| |
| s_logger.warn("Unsupported data object (" + dataObj.getType() + ", " + dataObj.getDataStore() + ")"); |
| return false; |
| } |
| |
| @Override |
| public boolean deleteIfNotReady(DataObject dataObj) { |
| long objId = dataObj.getId(); |
| DataStore dataStore = dataObj.getDataStore(); |
| if (dataStore.getRole() == DataStoreRole.Primary) { |
| if (dataObj.getType() == DataObjectType.TEMPLATE) { |
| VMTemplateStoragePoolVO destTmpltPool = templatePoolDao.findByPoolTemplate(dataStore.getId(), objId); |
| if (destTmpltPool != null && destTmpltPool.getState() != ObjectInDataStoreStateMachine.State.Ready) { |
| return templatePoolDao.remove(destTmpltPool.getId()); |
| } else { |
| s_logger.warn("Template " + objId + " is not found on storage pool " + dataStore.getId() + ", so no need to delete"); |
| return true; |
| } |
| } else if (dataObj.getType() == DataObjectType.SNAPSHOT) { |
| SnapshotDataStoreVO destSnapshotStore = snapshotDataStoreDao.findByStoreSnapshot(dataStore.getRole(), dataStore.getId(), objId); |
| if (destSnapshotStore != null && destSnapshotStore.getState() != ObjectInDataStoreStateMachine.State.Ready) { |
| snapshotDataStoreDao.remove(destSnapshotStore.getId()); |
| } |
| return true; |
| } |
| } else { |
| // Image store |
| switch (dataObj.getType()) { |
| case TEMPLATE: |
| return true; |
| case SNAPSHOT: |
| SnapshotDataStoreVO destSnapshotStore = snapshotDataStoreDao.findByStoreSnapshot(dataStore.getRole(), dataStore.getId(), objId); |
| if (destSnapshotStore != null && destSnapshotStore.getState() != ObjectInDataStoreStateMachine.State.Ready) { |
| return snapshotDataStoreDao.remove(destSnapshotStore.getId()); |
| } else { |
| s_logger.warn("Snapshot " + objId + " is not found on image store " + dataStore.getId() + ", so no need to delete"); |
| return true; |
| } |
| case VOLUME: |
| VolumeDataStoreVO destVolumeStore = volumeDataStoreDao.findByStoreVolume(dataStore.getId(), objId); |
| if (destVolumeStore != null && destVolumeStore.getState() != ObjectInDataStoreStateMachine.State.Ready) { |
| return volumeDataStoreDao.remove(destVolumeStore.getId()); |
| } else { |
| s_logger.warn("Volume " + objId + " is not found on image store " + dataStore.getId() + ", so no need to delete"); |
| return true; |
| } |
| } |
| } |
| |
| s_logger.warn("Unsupported data object (" + dataObj.getType() + ", " + dataObj.getDataStore() + "), no need to delete from object in store ref table"); |
| return false; |
| } |
| |
| @Override |
| public boolean update(DataObject data, Event event) throws NoTransitionException, ConcurrentOperationException { |
| DataObjectInStore obj = this.findObject(data, data.getDataStore()); |
| if (obj == null) { |
| throw new CloudRuntimeException("can't find mapping in ObjectInDataStore table for: " + data); |
| } |
| |
| boolean result = true; |
| if (data.getDataStore().getRole() == DataStoreRole.Image || data.getDataStore().getRole() == DataStoreRole.ImageCache) { |
| switch (data.getType()) { |
| case TEMPLATE: |
| result = this.stateMachines.transitTo(obj, event, null, templateDataStoreDao); |
| break; |
| case SNAPSHOT: |
| result = this.stateMachines.transitTo(obj, event, null, snapshotDataStoreDao); |
| break; |
| case VOLUME: |
| result = this.stateMachines.transitTo(obj, event, null, volumeDataStoreDao); |
| break; |
| } |
| } else if (data.getType() == DataObjectType.TEMPLATE && data.getDataStore().getRole() == DataStoreRole.Primary) { |
| |
| result = this.stateMachines.transitTo(obj, event, null, templatePoolDao); |
| |
| } else if (data.getType() == DataObjectType.SNAPSHOT && data.getDataStore().getRole() == DataStoreRole.Primary) { |
| result = this.stateMachines.transitTo(obj, event, null, snapshotDataStoreDao); |
| } else { |
| throw new CloudRuntimeException("Invalid data or store type: " + data.getType() + " " + data.getDataStore().getRole()); |
| } |
| |
| if (!result) { |
| throw new ConcurrentOperationException("Multiple threads are trying to update data object state, racing condition"); |
| } |
| return true; |
| } |
| |
| @Override |
| public DataObject get(DataObject dataObj, DataStore store) { |
| if (dataObj.getType() == DataObjectType.TEMPLATE) { |
| return imageFactory.getTemplate(dataObj, store); |
| } else if (dataObj.getType() == DataObjectType.VOLUME) { |
| return volumeFactory.getVolume(dataObj, store); |
| } else if (dataObj.getType() == DataObjectType.SNAPSHOT) { |
| return snapshotFactory.getSnapshot(dataObj, store); |
| } |
| |
| throw new CloudRuntimeException("unknown type"); |
| } |
| |
| @Override |
| public DataObjectInStore findObject(DataObject obj, DataStore store) { |
| return findObject(obj.getId(), obj.getType(), store.getId(), store.getRole()); |
| } |
| |
| @Override |
| public DataObjectInStore findObject(long objId, DataObjectType type, long dataStoreId, DataStoreRole role) { |
| DataObjectInStore vo = null; |
| if (role == DataStoreRole.Image || role == DataStoreRole.ImageCache) { |
| switch (type) { |
| case TEMPLATE: |
| vo = templateDataStoreDao.findByStoreTemplate(dataStoreId, objId); |
| break; |
| case SNAPSHOT: |
| vo = snapshotDataStoreDao.findByStoreSnapshot(role, dataStoreId, objId); |
| break; |
| case VOLUME: |
| vo = volumeDataStoreDao.findByStoreVolume(dataStoreId, objId); |
| break; |
| } |
| } else if (type == DataObjectType.TEMPLATE && role == DataStoreRole.Primary) { |
| vo = templatePoolDao.findByPoolTemplate(dataStoreId, objId); |
| } else if (type == DataObjectType.SNAPSHOT && role == DataStoreRole.Primary) { |
| vo = snapshotDataStoreDao.findByStoreSnapshot(role, dataStoreId, objId); |
| } else { |
| s_logger.debug("Invalid data or store type: " + type + " " + role); |
| throw new CloudRuntimeException("Invalid data or store type: " + type + " " + role); |
| } |
| |
| return vo; |
| |
| } |
| |
| @Override |
| public DataStore findStore(long objId, DataObjectType type, DataStoreRole role) { |
| DataStore store = null; |
| if (role == DataStoreRole.Image) { |
| DataObjectInStore vo = null; |
| switch (type) { |
| case TEMPLATE: |
| vo = templateDataStoreDao.findByTemplate(objId, role); |
| break; |
| case SNAPSHOT: |
| vo = snapshotDataStoreDao.findBySnapshot(objId, role); |
| break; |
| case VOLUME: |
| vo = volumeDataStoreDao.findByVolume(objId); |
| break; |
| } |
| if (vo != null) { |
| store = this.storeMgr.getDataStore(vo.getDataStoreId(), role); |
| } |
| } |
| return store; |
| } |
| |
| } |