mirror of https://github.com/apache/cloudstack.git
CLOUDSTACK-3886: Volume attach/detach implementation for ROOT disk
Implemented for Xen hypervisor only by now Unittests are included
This commit is contained in:
parent
af1eba2708
commit
3a889aa217
|
|
@ -48,7 +48,7 @@ public class AttachVolumeCmd extends BaseAsyncVolumeCmd {
|
|||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.LONG, description = "the ID of the device to map the volume to within the guest OS. "
|
||||
+ "If no deviceId is passed in, the next available deviceId will be chosen. " + "Possible values for a Linux OS are:" + "* 1 - /dev/xvdb" + "* 2 - /dev/xvdc"
|
||||
+ "If no deviceId is passed in, the next available deviceId will be chosen. " + "Possible values for a Linux OS are:" + "* 0 - /dev/xvda" + "* 1 - /dev/xvdb" + "* 2 - /dev/xvdc"
|
||||
+ "* 4 - /dev/xvde" + "* 5 - /dev/xvdf" + "* 6 - /dev/xvdg" + "* 7 - /dev/xvdh" + "* 8 - /dev/xvdi" + "* 9 - /dev/xvdj")
|
||||
private Long deviceId;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public interface VolumeOrchestrationService {
|
|||
|
||||
DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner);
|
||||
|
||||
VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, Volume rootVolumeOfVm, VolumeInfo volume, HypervisorType rootDiskHyperType) throws NoTransitionException;
|
||||
VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException;
|
||||
|
||||
void release(VirtualMachineProfile profile);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ import java.util.concurrent.ExecutionException;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
|
|
@ -58,6 +56,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
|||
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.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.DiskTO;
|
||||
|
|
@ -721,20 +720,20 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
}
|
||||
|
||||
@Override
|
||||
public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, Volume rootVolumeOfVm, VolumeInfo volume, HypervisorType rootDiskHyperType) throws NoTransitionException {
|
||||
public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException {
|
||||
VirtualMachineTemplate rootDiskTmplt = _entityMgr.findById(VirtualMachineTemplate.class, vm.getTemplateId());
|
||||
DataCenter dcVO = _entityMgr.findById(DataCenter.class, vm.getDataCenterId());
|
||||
Pod pod = _entityMgr.findById(Pod.class, vm.getPodIdToDeployIn());
|
||||
StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId());
|
||||
|
||||
ServiceOffering svo = _entityMgr.findById(ServiceOffering.class, vm.getServiceOfferingId());
|
||||
DiskOffering diskVO = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId());
|
||||
Long clusterId = (rootDiskPool == null ? null : rootDiskPool.getClusterId());
|
||||
Long clusterId = (storagePool == null ? null : storagePool.getClusterId());
|
||||
|
||||
VolumeInfo vol = null;
|
||||
if (volume.getState() == Volume.State.Allocated) {
|
||||
vol = createVolume(volume, vm, rootDiskTmplt, dcVO, pod, clusterId, svo, diskVO, new ArrayList<StoragePool>(), volume.getSize(), rootDiskHyperType);
|
||||
} else if (volume.getState() == Volume.State.Uploaded) {
|
||||
vol = copyVolume(rootDiskPool, volume, vm, rootDiskTmplt, dcVO, pod, diskVO, svo, rootDiskHyperType);
|
||||
vol = copyVolume(storagePool, volume, vm, rootDiskTmplt, dcVO, pod, diskVO, svo, rootDiskHyperType);
|
||||
if (vol != null) {
|
||||
// Moving of Volume is successful, decrement the volume resource count from secondary for an account and increment it into primary storage under same account.
|
||||
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, volume.getSize());
|
||||
|
|
@ -1234,6 +1233,12 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
}
|
||||
throw new CloudRuntimeException("Unable to prepare Volume for vm because DeployDestination is null, vm:" + vm);
|
||||
}
|
||||
|
||||
// don't allow to start vm that doesn't have a root volume
|
||||
if (_volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT).isEmpty()) {
|
||||
throw new CloudRuntimeException("Unable to prepare volumes for vm as ROOT volume is missing");
|
||||
}
|
||||
|
||||
List<VolumeVO> vols = _volsDao.findUsableVolumesForInstance(vm.getId());
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Checking if we need to prepare " + vols.size() + " volumes for " + vm);
|
||||
|
|
|
|||
|
|
@ -225,6 +225,9 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
volume.setDeviceId(deviceId);
|
||||
volume.setUpdated(new Date());
|
||||
volume.setAttached(new Date());
|
||||
if (deviceId == 0L) {
|
||||
volume.setVolumeType(Type.ROOT);
|
||||
}
|
||||
update(volumeId, volume);
|
||||
}
|
||||
|
||||
|
|
@ -235,6 +238,9 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
volume.setDeviceId(null);
|
||||
volume.setUpdated(new Date());
|
||||
volume.setAttached(null);
|
||||
if (findById(volumeId).getVolumeType() == Type.ROOT) {
|
||||
volume.setVolumeType(Type.DATADISK);
|
||||
}
|
||||
update(volumeId, volume);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@
|
|||
package com.cloud.deploy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ import javax.naming.ConfigurationException;
|
|||
|
||||
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||
|
|
@ -49,17 +49,17 @@ import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
|
|||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.Listener;
|
||||
import com.cloud.agent.api.AgentControlAnswer;
|
||||
import com.cloud.agent.api.AgentControlCommand;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.StartupCommand;
|
||||
import com.cloud.agent.api.StartupRoutingCommand;
|
||||
import com.cloud.agent.manager.allocator.HostAllocator;
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.Listener;
|
||||
import com.cloud.agent.api.AgentControlAnswer;
|
||||
import com.cloud.agent.api.AgentControlCommand;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.StartupCommand;
|
||||
import com.cloud.agent.api.StartupRoutingCommand;
|
||||
import com.cloud.agent.manager.allocator.HostAllocator;
|
||||
import com.cloud.capacity.CapacityManager;
|
||||
import com.cloud.capacity.dao.CapacityDao;
|
||||
import com.cloud.configuration.Config;
|
||||
|
|
@ -68,11 +68,11 @@ import com.cloud.dc.ClusterDetailsVO;
|
|||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.DedicatedResourceVO;
|
||||
import com.cloud.dc.DedicatedResourceVO;
|
||||
import com.cloud.dc.Pod;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.DedicatedResourceDao;
|
||||
import com.cloud.dc.dao.DedicatedResourceDao;
|
||||
import com.cloud.dc.dao.HostPodDao;
|
||||
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
||||
import com.cloud.deploy.DeploymentPlanner.PlannerResourceUsage;
|
||||
|
|
@ -80,7 +80,7 @@ import com.cloud.deploy.dao.PlannerHostReservationDao;
|
|||
import com.cloud.exception.AffinityConflictException;
|
||||
import com.cloud.exception.ConnectionException;
|
||||
import com.cloud.exception.InsufficientServerCapacityException;
|
||||
import com.cloud.gpu.GPU;
|
||||
import com.cloud.gpu.GPU;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.Status;
|
||||
|
|
@ -89,13 +89,13 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
|||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.org.Cluster;
|
||||
import com.cloud.org.Grouping;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.service.ServiceOfferingDetailsVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDetailsDao;
|
||||
import com.cloud.service.ServiceOfferingDetailsVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDetailsDao;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.ScopeType;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.StorageManager;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolHostVO;
|
||||
|
|
@ -117,7 +117,7 @@ import com.cloud.utils.db.SearchCriteria;
|
|||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.db.TransactionStatus;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.StateListener;
|
||||
import com.cloud.vm.DiskProfile;
|
||||
import com.cloud.vm.ReservationContext;
|
||||
|
|
@ -125,7 +125,7 @@ import com.cloud.vm.VMInstanceVO;
|
|||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachine.Event;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import com.cloud.vm.VirtualMachineProfile;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
|
||||
|
|
@ -1161,6 +1161,11 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy
|
|||
throw new CloudRuntimeException("Unable to create deployment, no usable volumes found for the VM");
|
||||
}
|
||||
|
||||
// don't allow to start vm that doesn't have a root volume
|
||||
if (_volsDao.findByInstanceAndType(vmProfile.getId(), Volume.Type.ROOT).isEmpty()) {
|
||||
throw new CloudRuntimeException("Unable to prepare volumes for vm as ROOT volume is missing");
|
||||
}
|
||||
|
||||
// for each volume find list of suitable storage pools by calling the
|
||||
// allocators
|
||||
Set<Long> originalAvoidPoolSet = avoid.getPoolsToAvoid();
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ import java.util.concurrent.ExecutionException;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
|
||||
|
|
@ -70,6 +68,7 @@ import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
|
|||
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
|
||||
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
|
|
@ -146,82 +145,74 @@ import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
|||
|
||||
public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiService, VmWorkJobHandler {
|
||||
private final static Logger s_logger = Logger.getLogger(VolumeApiServiceImpl.class);
|
||||
|
||||
public static final String VM_WORK_JOB_HANDLER = VolumeApiServiceImpl.class.getSimpleName();
|
||||
|
||||
@Inject
|
||||
private VolumeOrchestrationService _volumeMgr;
|
||||
|
||||
VolumeOrchestrationService _volumeMgr;
|
||||
@Inject
|
||||
private EntityManager _entityMgr;
|
||||
EntityManager _entityMgr;
|
||||
@Inject
|
||||
private AgentManager _agentMgr;
|
||||
AgentManager _agentMgr;
|
||||
@Inject
|
||||
private TemplateManager _tmpltMgr;
|
||||
TemplateManager _tmpltMgr;
|
||||
@Inject
|
||||
private AsyncJobManager _asyncMgr;
|
||||
SnapshotManager _snapshotMgr;
|
||||
@Inject
|
||||
private SnapshotManager _snapshotMgr;
|
||||
AccountManager _accountMgr;
|
||||
@Inject
|
||||
private AccountManager _accountMgr;
|
||||
ConfigurationManager _configMgr;
|
||||
@Inject
|
||||
private ConfigurationManager _configMgr;
|
||||
VolumeDao _volsDao;
|
||||
@Inject
|
||||
private VolumeDao _volsDao;
|
||||
HostDao _hostDao;
|
||||
@Inject
|
||||
private HostDao _hostDao;
|
||||
SnapshotDao _snapshotDao;
|
||||
@Inject
|
||||
private SnapshotDao _snapshotDao;
|
||||
@Inject
|
||||
protected ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
|
||||
ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
|
||||
@Inject
|
||||
StoragePoolDetailsDao storagePoolDetailsDao;
|
||||
@Inject
|
||||
private UserVmDao _userVmDao;
|
||||
UserVmDao _userVmDao;
|
||||
@Inject
|
||||
VolumeDataStoreDao _volumeStoreDao;
|
||||
@Inject
|
||||
private VMInstanceDao _vmInstanceDao;
|
||||
VMInstanceDao _vmInstanceDao;
|
||||
@Inject
|
||||
private final PrimaryDataStoreDao _storagePoolDao = null;
|
||||
PrimaryDataStoreDao _storagePoolDao;
|
||||
@Inject
|
||||
private DiskOfferingDao _diskOfferingDao;
|
||||
DiskOfferingDao _diskOfferingDao;
|
||||
@Inject
|
||||
private AccountDao _accountDao;
|
||||
AccountDao _accountDao;
|
||||
@Inject
|
||||
private final DataCenterDao _dcDao = null;
|
||||
final DataCenterDao _dcDao = null;
|
||||
@Inject
|
||||
private VMTemplateDao _templateDao;
|
||||
VMTemplateDao _templateDao;
|
||||
@Inject
|
||||
private VolumeDao _volumeDao;
|
||||
ResourceLimitService _resourceLimitMgr;
|
||||
@Inject
|
||||
private ResourceLimitService _resourceLimitMgr;
|
||||
VmDiskStatisticsDao _vmDiskStatsDao;
|
||||
@Inject
|
||||
private VmDiskStatisticsDao _vmDiskStatsDao;
|
||||
VMSnapshotDao _vmSnapshotDao;
|
||||
@Inject
|
||||
private VMSnapshotDao _vmSnapshotDao;
|
||||
ConfigurationDao _configDao;
|
||||
@Inject
|
||||
DataStoreManager dataStoreMgr;
|
||||
@Inject
|
||||
VolumeService volService;
|
||||
@Inject
|
||||
VolumeDataFactory volFactory;
|
||||
@Inject
|
||||
SnapshotApiService snapshotMgr;
|
||||
@Inject
|
||||
UUIDManager _uuidMgr;
|
||||
@Inject
|
||||
HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
|
||||
@Inject
|
||||
AsyncJobManager _jobMgr;
|
||||
@Inject
|
||||
VmWorkJobDao _workJobDao;
|
||||
|
||||
private List<StoragePoolAllocator> _storagePoolAllocators;
|
||||
@Inject
|
||||
private ConfigurationDao _configDao;
|
||||
@Inject
|
||||
private DataStoreManager dataStoreMgr;
|
||||
@Inject
|
||||
private VolumeService volService;
|
||||
@Inject
|
||||
private VolumeDataFactory volFactory;
|
||||
@Inject
|
||||
private SnapshotApiService snapshotMgr;
|
||||
@Inject
|
||||
private UUIDManager _uuidMgr;
|
||||
|
||||
@Inject
|
||||
private HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
|
||||
|
||||
@Inject
|
||||
private AsyncJobManager _jobMgr;
|
||||
|
||||
@Inject
|
||||
protected VmWorkJobDao _workJobDao;
|
||||
|
||||
VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this);
|
||||
|
||||
|
|
@ -848,7 +839,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
else if (jobResult instanceof Throwable)
|
||||
throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
|
||||
else if (jobResult instanceof Long) {
|
||||
vol = _volumeDao.findById((Long)jobResult);
|
||||
vol = _volsDao.findById((Long)jobResult);
|
||||
}
|
||||
}
|
||||
return volume;
|
||||
|
|
@ -1051,7 +1042,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
else if (jobResult instanceof Throwable)
|
||||
throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
|
||||
else if (jobResult instanceof Long) {
|
||||
vol = _volumeDao.findById((Long)jobResult);
|
||||
vol = _volsDao.findById((Long) jobResult);
|
||||
}
|
||||
}
|
||||
return vol;
|
||||
|
|
@ -1067,19 +1058,19 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
||||
// Check that the volume ID is valid
|
||||
VolumeInfo volume = volFactory.getVolume(volumeId);
|
||||
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
|
||||
// Check that the volume is a data volume
|
||||
if (volume == null || volume.getVolumeType() != Volume.Type.DATADISK) {
|
||||
throw new InvalidParameterValueException("Please specify a valid data volume.");
|
||||
if (volumeToAttach == null || !(volumeToAttach.getVolumeType() == Volume.Type.DATADISK || volumeToAttach.getVolumeType() == Volume.Type.ROOT)) {
|
||||
throw new InvalidParameterValueException("Please specify a volume with the valid type: " + Volume.Type.ROOT.toString() + " or " + Volume.Type.DATADISK.toString());
|
||||
}
|
||||
|
||||
// Check that the volume is not currently attached to any VM
|
||||
if (volume.getInstanceId() != null) {
|
||||
if (volumeToAttach.getInstanceId() != null) {
|
||||
throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM.");
|
||||
}
|
||||
|
||||
// Check that the volume is not destroyed
|
||||
if (volume.getState() == Volume.State.Destroy) {
|
||||
if (volumeToAttach.getState() == Volume.State.Destroy) {
|
||||
throw new InvalidParameterValueException("Please specify a volume that is not destroyed.");
|
||||
}
|
||||
|
||||
|
|
@ -1094,31 +1085,46 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
|
||||
}
|
||||
|
||||
// Check that the VM and the volume are in the same zone
|
||||
if (vm.getDataCenterId() != volumeToAttach.getDataCenterId()) {
|
||||
throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume.");
|
||||
}
|
||||
|
||||
// permission check
|
||||
_accountMgr.checkAccess(caller, null, true, volumeToAttach, vm);
|
||||
|
||||
// Check that the device ID is valid
|
||||
if (deviceId != null) {
|
||||
// validate ROOT volume type
|
||||
if (deviceId.longValue() == 0) {
|
||||
throw new InvalidParameterValueException("deviceId can't be 0, which is used by Root device");
|
||||
validateRootVolumeDetachAttach(_volsDao.findById(volumeToAttach.getId()), vm);
|
||||
// vm shouldn't have any volume with deviceId 0
|
||||
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
|
||||
throw new InvalidParameterValueException("Vm already has root volume attached to it");
|
||||
}
|
||||
// volume can't be in Uploaded state
|
||||
if (volumeToAttach.getState() == Volume.State.Uploaded) {
|
||||
throw new InvalidParameterValueException("No support for Root volume attach in state " + Volume.State.Uploaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the number of data volumes attached to VM is less than
|
||||
// that supported by hypervisor
|
||||
List<VolumeVO> existingDataVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
|
||||
int maxDataVolumesSupported = getMaxDataVolumesSupported(vm);
|
||||
if (existingDataVolumes.size() >= maxDataVolumesSupported) {
|
||||
throw new InvalidParameterValueException("The specified VM already has the maximum number of data disks (" + maxDataVolumesSupported + "). Please specify another VM.");
|
||||
}
|
||||
|
||||
// Check that the VM and the volume are in the same zone
|
||||
if (vm.getDataCenterId() != volume.getDataCenterId()) {
|
||||
throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume.");
|
||||
if (deviceId == null || deviceId.longValue() != 0) {
|
||||
List<VolumeVO> existingDataVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
|
||||
int maxDataVolumesSupported = getMaxDataVolumesSupported(vm);
|
||||
if (existingDataVolumes.size() >= maxDataVolumesSupported) {
|
||||
throw new InvalidParameterValueException("The specified VM already has the maximum number of data disks (" + maxDataVolumesSupported + "). Please specify another VM.");
|
||||
}
|
||||
}
|
||||
deviceId = getDeviceId(vmId, deviceId);
|
||||
|
||||
// If local storage is disabled then attaching a volume with local disk
|
||||
// offering not allowed
|
||||
DataCenterVO dataCenter = _dcDao.findById(volume.getDataCenterId());
|
||||
DataCenterVO dataCenter = _dcDao.findById(volumeToAttach.getDataCenterId());
|
||||
if (!dataCenter.isLocalStorageEnabled()) {
|
||||
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
|
||||
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
|
||||
if (diskOffering.getUseLocalStorage()) {
|
||||
throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it");
|
||||
}
|
||||
|
|
@ -1130,48 +1136,53 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
throw new InvalidParameterValueException("Unable to attach volume, please specify a VM that does not have VM snapshots");
|
||||
}
|
||||
|
||||
// permission check
|
||||
_accountMgr.checkAccess(caller, null, true, volume, vm);
|
||||
|
||||
if (!(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()) || Volume.State.Uploaded.equals(volume.getState()))) {
|
||||
if (!(Volume.State.Allocated.equals(volumeToAttach.getState()) || Volume.State.Ready.equals(volumeToAttach.getState()) || Volume.State.Uploaded.equals(volumeToAttach.getState()))) {
|
||||
throw new InvalidParameterValueException("Volume state must be in Allocated, Ready or in Uploaded state");
|
||||
}
|
||||
|
||||
VolumeVO rootVolumeOfVm = null;
|
||||
VolumeVO exstingVolumeOfVm = null;
|
||||
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
|
||||
if (rootVolumesOfVm.size() != 1) {
|
||||
if (rootVolumesOfVm.size() > 1) {
|
||||
throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
|
||||
} else {
|
||||
rootVolumeOfVm = rootVolumesOfVm.get(0);
|
||||
}
|
||||
|
||||
HypervisorType rootDiskHyperType = vm.getHypervisorType();
|
||||
|
||||
HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume.getId());
|
||||
|
||||
VolumeVO dataDiskVol = _volsDao.findById(volume.getId());
|
||||
StoragePoolVO dataDiskStoragePool = _storagePoolDao.findById(dataDiskVol.getPoolId());
|
||||
|
||||
// managed storage can be used for different types of hypervisors
|
||||
// only perform this check if the volume's storage pool is not null and not managed
|
||||
if (dataDiskStoragePool != null && !dataDiskStoragePool.isManaged()) {
|
||||
if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) {
|
||||
throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm");
|
||||
if (!rootVolumesOfVm.isEmpty()) {
|
||||
exstingVolumeOfVm = rootVolumesOfVm.get(0);
|
||||
} else {
|
||||
// locate data volume of the vm
|
||||
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
|
||||
for (VolumeVO diskVolume : diskVolumesOfVm) {
|
||||
if (diskVolume.getState() != Volume.State.Allocated) {
|
||||
exstingVolumeOfVm = diskVolume;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviceId = getDeviceId(vmId, deviceId);
|
||||
VolumeInfo volumeOnPrimaryStorage = volume;
|
||||
//don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
|
||||
List<VolumeVO> existingRootVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
|
||||
boolean rootVolumeCreatedOnPrimary = true;
|
||||
if (existingRootVolumes.get(0).getState().equals(Volume.State.Allocated)) {
|
||||
rootVolumeCreatedOnPrimary = false;
|
||||
HypervisorType rootDiskHyperType = vm.getHypervisorType();
|
||||
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
|
||||
|
||||
StoragePoolVO volumeToAttachStoragePool = _storagePoolDao.findById(volumeToAttach.getPoolId());
|
||||
|
||||
// managed storage can be used for different types of hypervisors
|
||||
// only perform this check if the volume's storage pool is not null and not managed
|
||||
if (volumeToAttachStoragePool != null && !volumeToAttachStoragePool.isManaged()) {
|
||||
if (volumeToAttachHyperType != HypervisorType.None && rootDiskHyperType != volumeToAttachHyperType) {
|
||||
throw new InvalidParameterValueException("Can't attach a volume created by: " + volumeToAttachHyperType + " to a " + rootDiskHyperType + " vm");
|
||||
}
|
||||
}
|
||||
|
||||
if ((volume.getState().equals(Volume.State.Allocated) && rootVolumeCreatedOnPrimary) || volume.getState() == Volume.State.Uploaded) {
|
||||
VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;
|
||||
|
||||
//don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
|
||||
StoragePoolVO destPrimaryStorage = null;
|
||||
if (exstingVolumeOfVm != null && !exstingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
|
||||
destPrimaryStorage = _storagePoolDao.findById(exstingVolumeOfVm.getPoolId());
|
||||
}
|
||||
|
||||
if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeToAttach.getState() == Volume.State.Uploaded)) {
|
||||
try {
|
||||
volumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, rootVolumeOfVm, volume, rootDiskHyperType);
|
||||
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType, destPrimaryStorage);
|
||||
} catch (NoTransitionException e) {
|
||||
s_logger.debug("Failed to create volume on primary storage", e);
|
||||
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
|
||||
|
|
@ -1179,20 +1190,20 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
|
||||
// reload the volume from db
|
||||
volumeOnPrimaryStorage = volFactory.getVolume(volumeOnPrimaryStorage.getId());
|
||||
boolean moveVolumeNeeded = needMoveVolume(rootVolumeOfVm, volumeOnPrimaryStorage);
|
||||
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
|
||||
boolean moveVolumeNeeded = needMoveVolume(exstingVolumeOfVm, newVolumeOnPrimaryStorage);
|
||||
|
||||
if (moveVolumeNeeded) {
|
||||
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)volumeOnPrimaryStorage.getDataStore();
|
||||
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)newVolumeOnPrimaryStorage.getDataStore();
|
||||
if (primaryStore.isLocal()) {
|
||||
throw new CloudRuntimeException("Failed to attach local data volume " + volume.getName() + " to VM " + vm.getDisplayName()
|
||||
throw new CloudRuntimeException("Failed to attach local data volume " + volumeToAttach.getName() + " to VM " + vm.getDisplayName()
|
||||
+ " as migration of local data volume is not allowed");
|
||||
}
|
||||
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId());
|
||||
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(exstingVolumeOfVm.getPoolId());
|
||||
|
||||
try {
|
||||
volumeOnPrimaryStorage = _volumeMgr.moveVolume(volumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(),
|
||||
vmRootVolumePool.getClusterId(), dataDiskHyperType);
|
||||
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(),
|
||||
vmRootVolumePool.getClusterId(), volumeToAttachHyperType);
|
||||
} catch (ConcurrentOperationException e) {
|
||||
s_logger.debug("move volume failed", e);
|
||||
throw new CloudRuntimeException("move volume failed", e);
|
||||
|
|
@ -1211,10 +1222,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
s_logger.info("Trying to attaching volume " + volumeId + " to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status");
|
||||
}
|
||||
|
||||
_asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);
|
||||
_jobMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);
|
||||
}
|
||||
|
||||
VolumeVO newVol = _volumeDao.findById(volumeOnPrimaryStorage.getId());
|
||||
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
|
||||
newVol = sendAttachVolumeCommand(vm, newVol, deviceId);
|
||||
return newVol;
|
||||
}
|
||||
|
|
@ -1223,7 +1234,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPDATE, eventDescription = "updating volume", async = true)
|
||||
public Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, String customId, long entityOwnerId, String chainInfo) {
|
||||
|
||||
VolumeVO volume = _volumeDao.findById(volumeId);
|
||||
VolumeVO volume = _volsDao.findById(volumeId);
|
||||
|
||||
if(volume == null)
|
||||
throw new InvalidParameterValueException("The volume id doesn't exist");
|
||||
|
|
@ -1259,7 +1270,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
updateDisplay(volume, displayVolume);
|
||||
|
||||
_volumeDao.update(volumeId, volume);
|
||||
_volsDao.update(volumeId, volume);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
|
@ -1277,7 +1288,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
if (displayVolume != null && displayVolume != volume.isDisplayVolume()){
|
||||
// FIXME - Confused - typecast for now.
|
||||
((VolumeVO)volume).setDisplayVolume(displayVolume);
|
||||
_volumeDao.update(volume.getId(), (VolumeVO)volume);
|
||||
_volsDao.update(volume.getId(), (VolumeVO) volume);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1347,10 +1358,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
// Permissions check
|
||||
_accountMgr.checkAccess(caller, null, true, volume);
|
||||
|
||||
// Check that the volume is a data volume
|
||||
if (volume.getVolumeType() != Volume.Type.DATADISK) {
|
||||
throw new InvalidParameterValueException("Please specify a data volume.");
|
||||
}
|
||||
|
||||
// Check that the volume is currently attached to a VM
|
||||
if (vmId == null) {
|
||||
|
|
@ -1363,6 +1370,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
|
||||
}
|
||||
|
||||
// Check that the volume is a data/root volume
|
||||
if (!(volume.getVolumeType() == Volume.Type.ROOT || volume.getVolumeType() == Volume.Type.DATADISK)) {
|
||||
throw new InvalidParameterValueException("Please specify volume of type " + Volume.Type.DATADISK.toString() + " or " + Volume.Type.ROOT.toString());
|
||||
}
|
||||
|
||||
// Root volume detach is allowed for following hypervisors: Xen/KVM/VmWare
|
||||
if (volume.getVolumeType() == Volume.Type.ROOT) {
|
||||
validateRootVolumeDetachAttach(volume, vm);
|
||||
}
|
||||
|
||||
// Don't allow detach if target VM has associated VM snapshots
|
||||
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
|
||||
if (vmSnapshots.size() > 0) {
|
||||
|
|
@ -1377,7 +1394,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
s_logger.info("Trying to attaching volume " + volumeId + "to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status");
|
||||
}
|
||||
|
||||
_asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);
|
||||
_jobMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);
|
||||
}
|
||||
|
||||
AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
|
||||
|
|
@ -1388,7 +1405,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
placeHolder = createPlaceHolderWork(vmId);
|
||||
}
|
||||
try {
|
||||
return orchestrateDetachVolumeFromVM(vmId, volumeId);
|
||||
return orchestrateDetachVolumeFromVM(vmId, volumeId);
|
||||
} finally {
|
||||
if (VmJobEnabled.value())
|
||||
_workJobDao.expunge(placeHolder.getId());
|
||||
|
|
@ -1412,16 +1429,32 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
else if (jobResult instanceof Throwable)
|
||||
throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
|
||||
else if (jobResult instanceof Long) {
|
||||
vol = _volumeDao.findById((Long)jobResult);
|
||||
vol = _volsDao.findById((Long) jobResult);
|
||||
}
|
||||
}
|
||||
return vol;
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRootVolumeDetachAttach(VolumeVO volume, UserVmVO vm) {
|
||||
if (!(vm.getHypervisorType() == HypervisorType.XenServer)) {
|
||||
throw new InvalidParameterValueException("Root volume detach is allowed for hypervisor type " + HypervisorType.XenServer + " only");
|
||||
}
|
||||
if (!(vm.getState() == State.Stopped) || (vm.getState() == State.Destroyed)) {
|
||||
throw new InvalidParameterValueException("Root volume detach can happen only when vm is in states: " + State.Stopped.toString() + " or " + State.Destroyed.toString());
|
||||
}
|
||||
|
||||
if (volume.getPoolId() != null) {
|
||||
StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId());
|
||||
if (pool.isManaged()) {
|
||||
throw new InvalidParameterValueException("Root volume detach is not supported for Managed DataStores");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Volume orchestrateDetachVolumeFromVM(long vmId, long volumeId) {
|
||||
|
||||
Volume volume = _volumeDao.findById(volumeId);
|
||||
Volume volume = _volsDao.findById(volumeId);
|
||||
VMInstanceVO vm = _vmInstanceDao.findById(vmId);
|
||||
|
||||
String errorMsg = "Failed to detach volume " + volume.getName() + " from VM " + vm.getHostName();
|
||||
|
|
@ -1782,7 +1815,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
throw new PermissionDeniedException("Extraction has been disabled by admin");
|
||||
}
|
||||
|
||||
VolumeVO volume = _volumeDao.findById(volumeId);
|
||||
VolumeVO volume = _volsDao.findById(volumeId);
|
||||
if (volume == null) {
|
||||
InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find volume with specified volumeId");
|
||||
ex.addProxyObject(volumeId.toString(), "volumeId");
|
||||
|
|
@ -1877,7 +1910,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
@Override
|
||||
public boolean isDisplayResourceEnabled(Long id) {
|
||||
Volume volume = _volumeDao.findById(id);
|
||||
Volume volume = _volsDao.findById(id);
|
||||
if (volume == null) {
|
||||
return true; // bad id given, default to true
|
||||
}
|
||||
|
|
@ -1902,55 +1935,55 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
}
|
||||
|
||||
private boolean needMoveVolume(VolumeVO rootVolumeOfVm, VolumeInfo volume) {
|
||||
if (rootVolumeOfVm.getPoolId() == null || volume.getPoolId() == null) {
|
||||
private boolean needMoveVolume(VolumeVO existingVolume, VolumeInfo newVolume) {
|
||||
if (existingVolume == null || existingVolume.getPoolId() == null || newVolume.getPoolId() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataStore storeForRootVol = dataStoreMgr.getPrimaryDataStore(rootVolumeOfVm.getPoolId());
|
||||
DataStore storeForDataVol = dataStoreMgr.getPrimaryDataStore(volume.getPoolId());
|
||||
DataStore storeForExistingVol = dataStoreMgr.getPrimaryDataStore(existingVolume.getPoolId());
|
||||
DataStore storeForNewVol = dataStoreMgr.getPrimaryDataStore(newVolume.getPoolId());
|
||||
|
||||
Scope storeForRootStoreScope = storeForRootVol.getScope();
|
||||
if (storeForRootStoreScope == null) {
|
||||
throw new CloudRuntimeException("Can't get scope of data store: " + storeForRootVol.getId());
|
||||
Scope storeForExistingStoreScope = storeForExistingVol.getScope();
|
||||
if (storeForExistingStoreScope == null) {
|
||||
throw new CloudRuntimeException("Can't get scope of data store: " + storeForExistingVol.getId());
|
||||
}
|
||||
|
||||
Scope storeForDataStoreScope = storeForDataVol.getScope();
|
||||
if (storeForDataStoreScope == null) {
|
||||
throw new CloudRuntimeException("Can't get scope of data store: " + storeForDataVol.getId());
|
||||
Scope storeForNewStoreScope = storeForNewVol.getScope();
|
||||
if (storeForNewStoreScope == null) {
|
||||
throw new CloudRuntimeException("Can't get scope of data store: " + storeForNewVol.getId());
|
||||
}
|
||||
|
||||
if (storeForDataStoreScope.getScopeType() == ScopeType.ZONE) {
|
||||
if (storeForNewStoreScope.getScopeType() == ScopeType.ZONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (storeForRootStoreScope.getScopeType() != storeForDataStoreScope.getScopeType()) {
|
||||
if (storeForDataStoreScope.getScopeType() == ScopeType.CLUSTER) {
|
||||
if (storeForExistingStoreScope.getScopeType() != storeForNewStoreScope.getScopeType()) {
|
||||
if (storeForNewStoreScope.getScopeType() == ScopeType.CLUSTER) {
|
||||
Long vmClusterId = null;
|
||||
if (storeForRootStoreScope.getScopeType() == ScopeType.HOST) {
|
||||
HostScope hs = (HostScope)storeForRootStoreScope;
|
||||
if (storeForExistingStoreScope.getScopeType() == ScopeType.HOST) {
|
||||
HostScope hs = (HostScope)storeForExistingStoreScope;
|
||||
vmClusterId = hs.getClusterId();
|
||||
} else if (storeForRootStoreScope.getScopeType() == ScopeType.ZONE) {
|
||||
Long hostId = _vmInstanceDao.findById(rootVolumeOfVm.getInstanceId()).getHostId();
|
||||
} else if (storeForExistingStoreScope.getScopeType() == ScopeType.ZONE) {
|
||||
Long hostId = _vmInstanceDao.findById(existingVolume.getInstanceId()).getHostId();
|
||||
if (hostId != null) {
|
||||
HostVO host = _hostDao.findById(hostId);
|
||||
vmClusterId = host.getClusterId();
|
||||
}
|
||||
}
|
||||
if (storeForDataStoreScope.getScopeId().equals(vmClusterId)) {
|
||||
if (storeForNewStoreScope.getScopeId().equals(vmClusterId)) {
|
||||
return false;
|
||||
}
|
||||
} else if (storeForDataStoreScope.getScopeType() == ScopeType.HOST
|
||||
&& (storeForRootStoreScope.getScopeType() == ScopeType.CLUSTER || storeForRootStoreScope.getScopeType() == ScopeType.ZONE)) {
|
||||
Long hostId = _vmInstanceDao.findById(rootVolumeOfVm.getInstanceId()).getHostId();
|
||||
if (storeForDataStoreScope.getScopeId().equals(hostId)) {
|
||||
} else if (storeForNewStoreScope.getScopeType() == ScopeType.HOST
|
||||
&& (storeForExistingStoreScope.getScopeType() == ScopeType.CLUSTER || storeForExistingStoreScope.getScopeType() == ScopeType.ZONE)) {
|
||||
Long hostId = _vmInstanceDao.findById(existingVolume.getInstanceId()).getHostId();
|
||||
if (storeForNewStoreScope.getScopeId().equals(hostId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
throw new CloudRuntimeException("Can't move volume between scope: " + storeForDataStoreScope.getScopeType() + " and " + storeForRootStoreScope.getScopeType());
|
||||
throw new CloudRuntimeException("Can't move volume between scope: " + storeForNewStoreScope.getScopeType() + " and " + storeForExistingStoreScope.getScopeType());
|
||||
}
|
||||
|
||||
return !storeForRootStoreScope.isSameScope(storeForDataStoreScope);
|
||||
return !storeForExistingStoreScope.isSameScope(storeForNewStoreScope);
|
||||
}
|
||||
|
||||
private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, Long deviceId) {
|
||||
|
|
@ -1975,7 +2008,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
if (hostId != null) {
|
||||
host = _hostDao.findById(hostId);
|
||||
|
||||
if (host != null && host.getHypervisorType() == HypervisorType.XenServer && volumeToAttachStoragePool.isManaged()) {
|
||||
if (host != null && host.getHypervisorType() == HypervisorType.XenServer && volumeToAttachStoragePool != null && volumeToAttachStoragePool.isManaged()) {
|
||||
sendCommand = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2100,7 +2133,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
// allocate deviceId
|
||||
List<VolumeVO> vols = _volsDao.findByInstance(vmId);
|
||||
if (deviceId != null) {
|
||||
if (deviceId.longValue() > 15 || deviceId.longValue() == 0 || deviceId.longValue() == 3) {
|
||||
if (deviceId.longValue() > 15 || deviceId.longValue() == 3) {
|
||||
throw new RuntimeException("deviceId should be 1,2,4-15");
|
||||
}
|
||||
for (VolumeVO vol : vols) {
|
||||
|
|
@ -2161,7 +2194,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
@Override
|
||||
protected Volume retrieve() {
|
||||
return _volumeDao.findById(_volumeId);
|
||||
return _volsDao.findById(_volumeId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,316 @@
|
|||
package com.cloud.storage;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobJoinMapDao;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
|
||||
public class VolumeApiServiceImplTest {
|
||||
@Inject
|
||||
VolumeApiServiceImpl _svc = new VolumeApiServiceImpl();
|
||||
@Mock
|
||||
VolumeDao _volumeDao;
|
||||
@Mock
|
||||
AccountManager _accountMgr;
|
||||
@Mock
|
||||
UserVmDao _userVmDao;
|
||||
@Mock
|
||||
PrimaryDataStoreDao _storagePoolDao;
|
||||
@Mock
|
||||
VMSnapshotDao _vmSnapshotDao;
|
||||
@Mock
|
||||
AsyncJobManager _jobMgr;
|
||||
@Mock
|
||||
AsyncJobJoinMapDao _joinMapDao;
|
||||
@Mock
|
||||
VolumeDataFactory _volFactory;
|
||||
|
||||
@Mock
|
||||
VMInstanceDao _vmInstanceDao;
|
||||
|
||||
DetachVolumeCmd detachCmd = new DetachVolumeCmd();
|
||||
Class<?> _detachCmdClass = detachCmd.getClass();
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
_svc._volsDao = _volumeDao;
|
||||
_svc._accountMgr = _accountMgr;
|
||||
_svc._userVmDao = _userVmDao;
|
||||
_svc._storagePoolDao = _storagePoolDao;
|
||||
_svc._vmSnapshotDao = _vmSnapshotDao;
|
||||
_svc._vmInstanceDao = _vmInstanceDao;
|
||||
_svc._jobMgr = _jobMgr;
|
||||
_svc.volFactory = _volFactory;
|
||||
|
||||
// mock caller context
|
||||
AccountVO account = new AccountVO("admin", 1L, "networkDomain", Account.ACCOUNT_TYPE_NORMAL, "uuid");
|
||||
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString());
|
||||
CallContext.register(user, account);
|
||||
// mock async context
|
||||
AsyncJobExecutionContext context = new AsyncJobExecutionContext();
|
||||
AsyncJobExecutionContext.init(_svc._jobMgr, _joinMapDao);
|
||||
AsyncJobVO job = new AsyncJobVO();
|
||||
context.setJob(job);
|
||||
AsyncJobExecutionContext.setCurrentExecutionContext(context);
|
||||
|
||||
TransactionLegacy txn = TransactionLegacy.open("runVolumeDaoImplTest");
|
||||
try {
|
||||
// volume of running vm id=1
|
||||
VolumeVO volumeOfRunningVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 1L, "root", "root", 1, null,
|
||||
null, "root", Volume.Type.ROOT);
|
||||
when(_svc._volsDao.findById(1L)).thenReturn(volumeOfRunningVm);
|
||||
|
||||
UserVmVO runningVm = new UserVmVO(1L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false,
|
||||
false, 1L, 1L, 1L, null, "vm", null);
|
||||
runningVm.setState(State.Running);
|
||||
runningVm.setDataCenterId(1L);
|
||||
when(_svc._userVmDao.findById(1L)).thenReturn(runningVm);
|
||||
|
||||
// volume of stopped vm id=2
|
||||
VolumeVO volumeOfStoppedVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null,
|
||||
null, "root", Volume.Type.ROOT);
|
||||
volumeOfStoppedVm.setPoolId(1L);
|
||||
when(_svc._volsDao.findById(2L)).thenReturn(volumeOfStoppedVm);
|
||||
|
||||
UserVmVO stoppedVm = new UserVmVO(2L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false,
|
||||
false, 1L, 1L, 1L, null, "vm", null);
|
||||
stoppedVm.setState(State.Stopped);
|
||||
stoppedVm.setDataCenterId(1L);
|
||||
when(_svc._userVmDao.findById(2L)).thenReturn(stoppedVm);
|
||||
|
||||
|
||||
// volume of hyperV vm id=3
|
||||
UserVmVO hyperVVm = new UserVmVO(3L, "vm", "vm", 1, HypervisorType.Hyperv, 1L, false,
|
||||
false, 1L, 1L, 1L, null, "vm", null);
|
||||
hyperVVm.setState(State.Stopped);
|
||||
hyperVVm.setDataCenterId(1L);
|
||||
when(_svc._userVmDao.findById(3L)).thenReturn(hyperVVm);
|
||||
|
||||
VolumeVO volumeOfStoppeHyperVVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 3L, "root", "root", 1, null,
|
||||
null, "root", Volume.Type.ROOT);
|
||||
volumeOfStoppeHyperVVm.setPoolId(1L);
|
||||
when(_svc._volsDao.findById(3L)).thenReturn(volumeOfStoppeHyperVVm);
|
||||
|
||||
StoragePoolVO unmanagedPool = new StoragePoolVO();
|
||||
when(_svc._storagePoolDao.findById(1L)).thenReturn(unmanagedPool);
|
||||
|
||||
// volume of managed pool id=4
|
||||
StoragePoolVO managedPool = new StoragePoolVO();
|
||||
managedPool.setManaged(true);
|
||||
when(_svc._storagePoolDao.findById(2L)).thenReturn(managedPool);
|
||||
VolumeVO managedPoolVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null,
|
||||
null, "root", Volume.Type.ROOT);
|
||||
managedPoolVolume.setPoolId(2L);
|
||||
when(_svc._volsDao.findById(4L)).thenReturn(managedPoolVolume);
|
||||
|
||||
// non-root non-datadisk volume
|
||||
VolumeInfo volumeWithIncorrectVolumeType = Mockito.mock(VolumeInfo.class);
|
||||
when(volumeWithIncorrectVolumeType.getId()).thenReturn(5L);
|
||||
when(volumeWithIncorrectVolumeType.getVolumeType()).thenReturn(Volume.Type.ISO);
|
||||
when(_svc.volFactory.getVolume(5L)).thenReturn(volumeWithIncorrectVolumeType);
|
||||
|
||||
// correct root volume
|
||||
VolumeInfo correctRootVolume = Mockito.mock(VolumeInfo.class);
|
||||
when(correctRootVolume.getId()).thenReturn(6L);
|
||||
when(correctRootVolume.getDataCenterId()).thenReturn(1L);
|
||||
when(correctRootVolume.getVolumeType()).thenReturn(Volume.Type.ROOT);
|
||||
when(correctRootVolume.getInstanceId()).thenReturn(null);
|
||||
when(_svc.volFactory.getVolume(6L)).thenReturn(correctRootVolume);
|
||||
|
||||
VolumeVO correctRootVolumeVO = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null,
|
||||
null, "root", Volume.Type.ROOT);
|
||||
when(_svc._volsDao.findById(6L)).thenReturn(correctRootVolumeVO);
|
||||
|
||||
// managed root volume
|
||||
VolumeInfo managedVolume = Mockito.mock(VolumeInfo.class);
|
||||
when(managedVolume.getId()).thenReturn(7L);
|
||||
when(managedVolume.getDataCenterId()).thenReturn(1L);
|
||||
when(managedVolume.getVolumeType()).thenReturn(Volume.Type.ROOT);
|
||||
when(managedVolume.getInstanceId()).thenReturn(null);
|
||||
when(managedVolume.getPoolId()).thenReturn(2L);
|
||||
when(_svc.volFactory.getVolume(7L)).thenReturn(managedVolume);
|
||||
|
||||
VolumeVO managedVolume1 = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null,
|
||||
null, "root", Volume.Type.ROOT);
|
||||
managedVolume1.setPoolId(2L);
|
||||
managedVolume1.setDataCenterId(1L);
|
||||
when(_svc._volsDao.findById(7L)).thenReturn(managedVolume1);
|
||||
|
||||
// vm having root volume
|
||||
UserVmVO vmHavingRootVolume = new UserVmVO(4L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false,
|
||||
false, 1L, 1L, 1L, null, "vm", null);
|
||||
vmHavingRootVolume.setState(State.Stopped);
|
||||
vmHavingRootVolume.setDataCenterId(1L);
|
||||
when(_svc._userVmDao.findById(4L)).thenReturn(vmHavingRootVolume);
|
||||
List<VolumeVO> vols = new ArrayList<VolumeVO>();
|
||||
vols.add(new VolumeVO());
|
||||
when(_svc._volsDao.findByInstanceAndDeviceId(4L, 0L)).thenReturn(vols);
|
||||
|
||||
// volume in uploaded state
|
||||
VolumeInfo uploadedVolume = Mockito.mock(VolumeInfo.class);
|
||||
when(uploadedVolume.getId()).thenReturn(8L);
|
||||
when(uploadedVolume.getDataCenterId()).thenReturn(1L);
|
||||
when(uploadedVolume.getVolumeType()).thenReturn(Volume.Type.ROOT);
|
||||
when(uploadedVolume.getInstanceId()).thenReturn(null);
|
||||
when(uploadedVolume.getPoolId()).thenReturn(1L);
|
||||
when(uploadedVolume.getState()).thenReturn(Volume.State.Uploaded);
|
||||
when(_svc.volFactory.getVolume(8L)).thenReturn(uploadedVolume);
|
||||
|
||||
VolumeVO upVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null,
|
||||
null, "root", Volume.Type.ROOT);
|
||||
upVolume.setPoolId(1L);
|
||||
upVolume.setDataCenterId(1L);
|
||||
upVolume.setState(Volume.State.Uploaded);
|
||||
when(_svc._volsDao.findById(8L)).thenReturn(upVolume);
|
||||
|
||||
// helper dao methods mock
|
||||
when(_svc._vmSnapshotDao.findByVm(any(Long.class))).thenReturn(new ArrayList<VMSnapshotVO>());
|
||||
when(_svc._vmInstanceDao.findById(any(Long.class))).thenReturn(stoppedVm);
|
||||
|
||||
} finally {
|
||||
txn.close("runVolumeDaoImplTest");
|
||||
}
|
||||
|
||||
// helper methods mock
|
||||
doNothing().when(_svc._accountMgr).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class), any(ControlledEntity.class));
|
||||
doNothing().when(_svc._jobMgr).updateAsyncJobAttachment(any(Long.class), any(String.class), any(Long.class));
|
||||
when(_svc._jobMgr.submitAsyncJob(any(AsyncJobVO.class), any(String.class), any(Long.class))).thenReturn(1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* TESTS FOR DETACH ROOT VOLUME, COUNT=4
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDetachVolumeFromRunningVm() throws NoSuchFieldException, IllegalAccessException {
|
||||
Field dedicateIdField = _detachCmdClass.getDeclaredField("id");
|
||||
dedicateIdField.setAccessible(true);
|
||||
dedicateIdField.set(detachCmd, 1L);
|
||||
_svc.detachVolumeFromVM(detachCmd);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDetachVolumeFromStoppedHyperVVm() throws NoSuchFieldException, IllegalAccessException {
|
||||
Field dedicateIdField = _detachCmdClass.getDeclaredField("id");
|
||||
dedicateIdField.setAccessible(true);
|
||||
dedicateIdField.set(detachCmd, 3L);
|
||||
_svc.detachVolumeFromVM(detachCmd);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDetachVolumeOfManagedDataStore() throws NoSuchFieldException, IllegalAccessException {
|
||||
Field dedicateIdField = _detachCmdClass.getDeclaredField("id");
|
||||
dedicateIdField.setAccessible(true);
|
||||
dedicateIdField.set(detachCmd, 4L);
|
||||
_svc.detachVolumeFromVM(detachCmd);
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testDetachVolumeFromStoppedXenVm() throws NoSuchFieldException, IllegalAccessException {
|
||||
thrown.expect(NullPointerException.class);
|
||||
Field dedicateIdField = _detachCmdClass.getDeclaredField("id");
|
||||
dedicateIdField.setAccessible(true);
|
||||
dedicateIdField.set(detachCmd, 2L);
|
||||
_svc.detachVolumeFromVM(detachCmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* TESTS FOR ATTACH ROOT VOLUME, COUNT=7
|
||||
*/
|
||||
|
||||
// Negative test - try to attach non-root non-datadisk volume
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void attachIncorrectDiskType() throws NoSuchFieldException, IllegalAccessException {
|
||||
_svc.attachVolumeToVM(1L, 5L, 0L);
|
||||
}
|
||||
|
||||
// Negative test - attach root volume to running vm
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void attachRootDiskToRunningVm() throws NoSuchFieldException, IllegalAccessException {
|
||||
_svc.attachVolumeToVM(1L, 6L, 0L);
|
||||
}
|
||||
|
||||
// Negative test - attach root volume to non-xen vm
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void attachRootDiskToHyperVm() throws NoSuchFieldException, IllegalAccessException {
|
||||
_svc.attachVolumeToVM(3L, 6L, 0L);
|
||||
}
|
||||
|
||||
// Negative test - attach root volume from the managed data store
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void attachRootDiskOfManagedDataStore() throws NoSuchFieldException, IllegalAccessException {
|
||||
_svc.attachVolumeToVM(2L, 7L, 0L);
|
||||
}
|
||||
|
||||
// Negative test - root volume can't be attached to the vm already having a root volume attached
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void attachRootDiskToVmHavingRootDisk() throws NoSuchFieldException, IllegalAccessException {
|
||||
_svc.attachVolumeToVM(4L, 6L, 0L);
|
||||
}
|
||||
|
||||
// Negative test - root volume in uploaded state can't be attached
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void attachRootInUploadedState() throws NoSuchFieldException, IllegalAccessException {
|
||||
_svc.attachVolumeToVM(2L, 8L, 0L);
|
||||
}
|
||||
|
||||
// Positive test - attach ROOT volume in correct state, to the vm not having root volume attached
|
||||
@Test
|
||||
public void attachRootVolumePositive() throws NoSuchFieldException, IllegalAccessException {
|
||||
thrown.expect(NullPointerException.class);
|
||||
_svc.attachVolumeToVM(2L, 6L, 0L);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue