/** * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. * * This software is licensed under the GNU General Public License v3 or later. * * It is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ package com.cloud.storage.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import java.util.List; import javax.ejb.Local; import org.apache.log4j.Logger; import com.cloud.exception.ConcurrentOperationException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Volume; import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; import com.cloud.utils.Pair; import com.cloud.utils.db.Attribute; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine.State; @Local(value=VolumeDao.class) @DB(txn=false) public class VolumeDaoImpl extends GenericDaoBase implements VolumeDao { private static final Logger s_logger = Logger.getLogger(VolumeDaoImpl.class); protected final SearchBuilder DetachedAccountIdSearch; protected final SearchBuilder TemplateZoneSearch; protected final GenericSearchBuilder TotalSizeByPoolSearch; protected final GenericSearchBuilder ActiveTemplateSearch; protected final SearchBuilder InstanceStatesSearch; protected final SearchBuilder AllFieldsSearch; protected GenericSearchBuilder CountByAccount; protected final Attribute _stateAttr; protected static final String SELECT_VM_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ? and v.mirror_state = ?"; protected static final String SELECT_HYPERTYPE_FROM_VOLUME = "SELECT c.hypervisor_type from volumes v, storage_pool s, cluster c where v.pool_id = s.id and s.cluster_id = c.id and v.id = ?"; @Override public List findDetachedByAccount(long accountId) { SearchCriteria sc = DetachedAccountIdSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("destroyed", Volume.State.Destroy); return listBy(sc); } @Override public List findByAccount(long accountId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("state", Volume.State.Ready); return listBy(sc); } @Override public List findByInstance(long id) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", id); return listBy(sc); } @Override public List findByInstanceAndDeviceId(long instanceId, long deviceId){ SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", instanceId); sc.setParameters("deviceId", deviceId); return listBy(sc); } @Override public List findByPoolId(long poolId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("poolId", poolId); sc.setParameters("notDestroyed", Volume.State.Destroy); sc.setParameters("vType", Volume.Type.ROOT.toString()); return listBy(sc); } @Override public List findCreatedByInstance(long id) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", id); sc.setParameters("state", Volume.State.Ready); return listBy(sc); } @Override public List findUsableVolumesForInstance(long instanceId) { SearchCriteria sc = InstanceStatesSearch.create(); sc.setParameters("instance", instanceId); sc.setParameters("states", Volume.State.Creating, Volume.State.Ready, Volume.State.Allocated); return listBy(sc); } @Override public List findByInstanceAndType(long id, Type vType) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", id); sc.setParameters("vType", vType.toString()); return listBy(sc); } @Override public List findByInstanceIdDestroyed(long vmId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", vmId); sc.setParameters("destroyed", Volume.State.Destroy); return listBy(sc); } @Override public List findReadyRootVolumesByInstance(long instanceId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", instanceId); sc.setParameters("state", Volume.State.Ready); sc.setParameters("vType", Volume.Type.ROOT); return listBy(sc); } @Override public List findByAccountAndPod(long accountId, long podId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("pod", podId); sc.setParameters("state", Volume.State.Ready); return listIncludingRemovedBy(sc); } @Override public List findByTemplateAndZone(long templateId, long zoneId) { SearchCriteria sc = TemplateZoneSearch.create(); sc.setParameters("template", templateId); sc.setParameters("zone", zoneId); return listIncludingRemovedBy(sc); } @Override public boolean isAnyVolumeActivelyUsingTemplateOnPool(long templateId, long poolId) { SearchCriteria sc = ActiveTemplateSearch.create(); sc.setParameters("template", templateId); sc.setParameters("pool", poolId); List results = customSearchIncludingRemoved(sc, null); assert results.size() > 0 : "How can this return a size of " + results.size(); return results.get(0) > 0; } @Override public void deleteVolumesByInstance(long instanceId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", instanceId); expunge(sc); } @Override public void attachVolume(long volumeId, long vmId, long deviceId) { VolumeVO volume = createForUpdate(volumeId); volume.setInstanceId(vmId); volume.setDeviceId(deviceId); volume.setUpdated(new Date()); volume.setAttached(new Date()); update(volumeId, volume); } @Override public void detachVolume(long volumeId) { VolumeVO volume = createForUpdate(volumeId); volume.setInstanceId(null); volume.setDeviceId(null); volume.setUpdated(new Date()); volume.setAttached(null); update(volumeId, volume); } @Override public boolean update(VolumeVO vol, Volume.Event event) throws ConcurrentOperationException { Volume.State oldState = vol.getState(); Volume.State newState = oldState.getNextState(event); assert newState != null : "Event "+ event + " cannot happen from " + oldState; UpdateBuilder builder = getUpdateBuilder(vol); builder.set(vol, _stateAttr, newState); SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("id", vol.getId()); sc.setParameters("state", oldState); int rows = update(builder, sc, null); if (rows != 1) { VolumeVO dbVol = findById(vol.getId()); throw new ConcurrentOperationException("Unable to update " + vol + ": Old State=" + oldState + "; New State = " + newState + "; DB State=" + dbVol.getState()); } return rows == 1; } @Override @DB public HypervisorType getHypervisorType(long volumeId) { /*lookup from cluster of pool*/ Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; try { String sql = SELECT_HYPERTYPE_FROM_VOLUME; pstmt = txn.prepareAutoCloseStatement(sql); pstmt.setLong(1, volumeId); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return HypervisorType.getType(rs.getString(1)); } return HypervisorType.None; } catch (SQLException e) { throw new CloudRuntimeException("DB Exception on: " + SELECT_HYPERTYPE_FROM_VOLUME, e); } catch (Throwable e) { throw new CloudRuntimeException("Caught: " + SELECT_HYPERTYPE_FROM_VOLUME, e); } } @Override public ImageFormat getImageFormat(Long volumeId) { HypervisorType type = getHypervisorType(volumeId); if ( type.equals(HypervisorType.KVM)) { return ImageFormat.QCOW2; } else if ( type.equals(HypervisorType.XenServer)) { return ImageFormat.VHD; } else if ( type.equals(HypervisorType.VMware)) { return ImageFormat.OVA; } else { s_logger.warn("Do not support hypervisor " + type.toString()); return null; } } protected VolumeDaoImpl() { AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ); AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), Op.EQ); AllFieldsSearch.and("pod", AllFieldsSearch.entity().getPodId(), Op.EQ); AllFieldsSearch.and("instanceId", AllFieldsSearch.entity().getInstanceId(), Op.EQ); AllFieldsSearch.and("deviceId", AllFieldsSearch.entity().getDeviceId(), Op.EQ); AllFieldsSearch.and("poolId", AllFieldsSearch.entity().getPoolId(), Op.EQ); AllFieldsSearch.and("vType", AllFieldsSearch.entity().getVolumeType(), Op.EQ); AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.and("destroyed", AllFieldsSearch.entity().getState(), Op.EQ); AllFieldsSearch.and("notDestroyed", AllFieldsSearch.entity().getState(), Op.NEQ); AllFieldsSearch.done(); DetachedAccountIdSearch = createSearchBuilder(); DetachedAccountIdSearch.and("accountId", DetachedAccountIdSearch.entity().getAccountId(), Op.EQ); DetachedAccountIdSearch.and("destroyed", DetachedAccountIdSearch.entity().getState(), Op.NEQ); DetachedAccountIdSearch.and("instanceId", DetachedAccountIdSearch.entity().getInstanceId(), Op.NULL); DetachedAccountIdSearch.done(); TemplateZoneSearch = createSearchBuilder(); TemplateZoneSearch.and("template", TemplateZoneSearch.entity().getTemplateId(), Op.EQ); TemplateZoneSearch.and("zone", TemplateZoneSearch.entity().getDataCenterId(), Op.EQ); TemplateZoneSearch.done(); TotalSizeByPoolSearch = createSearchBuilder(SumCount.class); TotalSizeByPoolSearch.select("sum", Func.SUM, TotalSizeByPoolSearch.entity().getSize()); TotalSizeByPoolSearch.select("count", Func.COUNT, (Object[])null); TotalSizeByPoolSearch.and("poolId", TotalSizeByPoolSearch.entity().getPoolId(), Op.EQ); TotalSizeByPoolSearch.and("removed", TotalSizeByPoolSearch.entity().getRemoved(), Op.NULL); TotalSizeByPoolSearch.done(); ActiveTemplateSearch = createSearchBuilder(Long.class); ActiveTemplateSearch.and("pool", ActiveTemplateSearch.entity().getPoolId(), Op.EQ); ActiveTemplateSearch.and("template", ActiveTemplateSearch.entity().getTemplateId(), Op.EQ); ActiveTemplateSearch.and("removed", ActiveTemplateSearch.entity().getRemoved(), Op.NULL); ActiveTemplateSearch.select(null, Func.COUNT, null); ActiveTemplateSearch.done(); InstanceStatesSearch = createSearchBuilder(); InstanceStatesSearch.and("instance", InstanceStatesSearch.entity().getInstanceId(), Op.EQ); InstanceStatesSearch.and("states", InstanceStatesSearch.entity().getState(), Op.IN); InstanceStatesSearch.done(); CountByAccount = createSearchBuilder(Long.class); CountByAccount.select(null, Func.COUNT, null); CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.done(); _stateAttr = _allAttributes.get("state"); assert _stateAttr != null : "Couldn't get the state attribute"; } @Override @DB(txn=false) public Pair getCountAndTotalByPool(long poolId) { SearchCriteria sc = TotalSizeByPoolSearch.create(); sc.setParameters("poolId", poolId); List results = customSearchIncludingRemoved(sc, null); SumCount sumCount = results.get(0); return new Pair(sumCount.count, sumCount.sum); } @Override public Long countAllocatedVolumesForAccount(long accountId) { SearchCriteria sc = CountByAccount.create(); sc.setParameters("account", accountId); sc.setParameters("state", new Object[] {Volume.State.Destroy, State.Expunging}); return customSearch(sc, null).get(0); } public static class SumCount { public long sum; public long count; public SumCount() { } } @Override public List listVolumesToBeDestroyed() { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("state", Volume.State.Destroy); return listBy(sc); } }