Add storage migration

This commit is contained in:
Edison Su 2011-10-24 15:56:23 -07:00
parent fc3d93e3e7
commit 92eaf49f29
35 changed files with 735 additions and 277 deletions

View File

@ -27,6 +27,8 @@ import com.cloud.agent.api.ModifyStoragePoolCommand;
import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageSetupCommand;
import com.cloud.agent.api.SecStorageVMSetupCommand; import com.cloud.agent.api.SecStorageVMSetupCommand;
import com.cloud.agent.api.StoragePoolInfo; import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.CreateAnswer;
import com.cloud.agent.api.storage.CreateCommand; import com.cloud.agent.api.storage.CreateCommand;
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
@ -78,4 +80,6 @@ public interface MockStorageManager extends Manager {
public Answer CreatePrivateTemplateFromVolume(CreatePrivateTemplateFromVolumeCommand cmd); public Answer CreatePrivateTemplateFromVolume(CreatePrivateTemplateFromVolumeCommand cmd);
StoragePoolInfo getLocalStorage(String hostGuid, Long storageSize); StoragePoolInfo getLocalStorage(String hostGuid, Long storageSize);
CopyVolumeAnswer CopyVolume(CopyVolumeCommand cmd);
} }

View File

@ -44,6 +44,8 @@ import com.cloud.agent.api.SecStorageSetupAnswer;
import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageSetupCommand;
import com.cloud.agent.api.SecStorageVMSetupCommand; import com.cloud.agent.api.SecStorageVMSetupCommand;
import com.cloud.agent.api.StoragePoolInfo; import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.CreateAnswer;
import com.cloud.agent.api.storage.CreateCommand; import com.cloud.agent.api.storage.CreateCommand;
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
@ -670,5 +672,49 @@ public class MockStorageManagerImpl implements MockStorageManager {
return new CreatePrivateTemplateAnswer(cmd, true, "", template.getName(), template.getSize(), template.getSize(), template.getName(), ImageFormat.QCOW2); return new CreatePrivateTemplateAnswer(cmd, true, "", template.getName(), template.getSize(), template.getSize(), template.getName(), ImageFormat.QCOW2);
} }
@Override
public CopyVolumeAnswer CopyVolume(CopyVolumeCommand cmd) {
boolean toSecondaryStorage = cmd.toSecondaryStorage();
MockSecStorageVO sec = _mockSecStorageDao.findByUrl(cmd.getSecondaryStorageURL());
if (sec == null) {
return new CopyVolumeAnswer(cmd, false, "can't find secondary storage", null, null);
}
MockStoragePoolVO primaryStorage = _mockStoragePoolDao.findByUuid(cmd.getPool().getUuid());
if (primaryStorage == null) {
return new CopyVolumeAnswer(cmd, false, "Can't find primary storage", null, null);
}
MockVolumeVO volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath());
if (volume == null) {
return new CopyVolumeAnswer(cmd, false, "cant' find volume" + cmd.getVolumePath(), null, null);
}
String name = UUID.randomUUID().toString();
if (toSecondaryStorage) {
MockVolumeVO vol = new MockVolumeVO();
vol.setName(name);
vol.setPath(sec.getMountPoint() + name);
vol.setPoolId(sec.getId());
vol.setSize(volume.getSize());
vol.setStatus(Status.DOWNLOADED);
vol.setType(MockVolumeType.VOLUME);
vol = _mockVolumeDao.persist(vol);
return new CopyVolumeAnswer(cmd, true, null, sec.getMountPoint(), vol.getPath());
}
else {
MockVolumeVO vol = new MockVolumeVO();
vol.setName(name);
vol.setPath(primaryStorage.getMountPoint() + name);
vol.setPoolId(primaryStorage.getId());
vol.setSize(volume.getSize());
vol.setStatus(Status.DOWNLOADED);
vol.setType(MockVolumeType.VOLUME);
vol = _mockVolumeDao.persist(vol);
return new CopyVolumeAnswer(cmd, true, null, primaryStorage.getMountPoint(), vol.getPath());
}
}
} }

View File

@ -13,6 +13,8 @@ import java.util.Set;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.CheckVirtualMachineCommand;
import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.CleanupNetworkRulesCmd;
import com.cloud.agent.api.GetDomRVersionAnswer;
import com.cloud.agent.api.GetDomRVersionCmd;
import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.GetVmStatsCommand;
import com.cloud.agent.api.GetVncPortCommand; import com.cloud.agent.api.GetVncPortCommand;
import com.cloud.agent.api.MigrateAnswer; import com.cloud.agent.api.MigrateAnswer;
@ -80,5 +82,6 @@ public interface MockVmManager extends Manager {
HashMap<String, Pair<Long, Long>> syncNetworkGroups(SimulatorInfo info); HashMap<String, Pair<Long, Long>> syncNetworkGroups(SimulatorInfo info);
SecurityIngressRuleAnswer AddSecurityIngressRules(SecurityIngressRulesCmd cmd, SimulatorInfo info); SecurityIngressRuleAnswer AddSecurityIngressRules(SecurityIngressRulesCmd cmd, SimulatorInfo info);
MigrateAnswer Migrate(MigrateCommand cmd, SimulatorInfo info); MigrateAnswer Migrate(MigrateCommand cmd, SimulatorInfo info);
GetDomRVersionAnswer getDomRVersion(GetDomRVersionCmd cmd);
} }

View File

@ -21,6 +21,8 @@ import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckVirtualMachineAnswer; import com.cloud.agent.api.CheckVirtualMachineAnswer;
import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.CheckVirtualMachineCommand;
import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.CleanupNetworkRulesCmd;
import com.cloud.agent.api.GetDomRVersionAnswer;
import com.cloud.agent.api.GetDomRVersionCmd;
import com.cloud.agent.api.GetVmStatsAnswer; import com.cloud.agent.api.GetVmStatsAnswer;
import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.GetVmStatsCommand;
import com.cloud.agent.api.GetVncPortAnswer; import com.cloud.agent.api.GetVncPortAnswer;
@ -352,6 +354,11 @@ public class MockVmManagerImpl implements MockVmManager {
return Answer.createUnsupportedCommandAnswer(cmd); return Answer.createUnsupportedCommandAnswer(cmd);
} }
@Override
public GetDomRVersionAnswer getDomRVersion(GetDomRVersionCmd cmd) {
return new GetDomRVersionAnswer(cmd, null, null, null);
}
@Override @Override
public SecurityIngressRuleAnswer AddSecurityIngressRules(SecurityIngressRulesCmd cmd, SimulatorInfo info) { public SecurityIngressRuleAnswer AddSecurityIngressRules(SecurityIngressRulesCmd cmd, SimulatorInfo info) {
if (!info.isEnabled()) { if (!info.isEnabled()) {

View File

@ -17,6 +17,7 @@ import com.cloud.agent.api.AttachVolumeCommand;
import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.BackupSnapshotCommand;
import com.cloud.agent.api.CheckHealthCommand; import com.cloud.agent.api.CheckHealthCommand;
import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.CleanupNetworkRulesCmd;
import com.cloud.agent.api.ClusterSyncCommand;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;
import com.cloud.agent.api.ComputeChecksumCommand; import com.cloud.agent.api.ComputeChecksumCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
@ -25,6 +26,7 @@ import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
import com.cloud.agent.api.DeleteSnapshotBackupCommand; import com.cloud.agent.api.DeleteSnapshotBackupCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand; import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.GetDomRVersionCmd;
import com.cloud.agent.api.GetHostStatsCommand; import com.cloud.agent.api.GetHostStatsCommand;
import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.GetStorageStatsCommand;
import com.cloud.agent.api.GetVmStatsCommand; import com.cloud.agent.api.GetVmStatsCommand;
@ -52,6 +54,7 @@ import com.cloud.agent.api.routing.SavePasswordCommand;
import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand;
import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand;
import com.cloud.agent.api.routing.VmDataCommand; import com.cloud.agent.api.routing.VmDataCommand;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreateCommand; import com.cloud.agent.api.storage.CreateCommand;
import com.cloud.agent.api.storage.DeleteTemplateCommand; import com.cloud.agent.api.storage.DeleteTemplateCommand;
import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.storage.DestroyCommand;
@ -260,6 +263,12 @@ public class SimulatorManagerImpl implements SimulatorManager {
return _mockAgentMgr.MaintainCommand((MaintainCommand)cmd); return _mockAgentMgr.MaintainCommand((MaintainCommand)cmd);
} else if (cmd instanceof GetVmStatsCommand) { } else if (cmd instanceof GetVmStatsCommand) {
return _mockVmMgr.getVmStats((GetVmStatsCommand)cmd); return _mockVmMgr.getVmStats((GetVmStatsCommand)cmd);
} else if (cmd instanceof GetDomRVersionCmd) {
return _mockVmMgr.getDomRVersion((GetDomRVersionCmd)cmd);
} else if (cmd instanceof ClusterSyncCommand) {
return new Answer(cmd);
} else if (cmd instanceof CopyVolumeCommand) {
return _mockStorageMgr.CopyVolume((CopyVolumeCommand)cmd);
} else { } else {
return Answer.createUnsupportedCommandAnswer(cmd); return Answer.createUnsupportedCommandAnswer(cmd);
} }

View File

@ -71,5 +71,12 @@ public class MockVm {
public int getVncPort() { public int getVncPort() {
return vncPort; return vncPort;
} }
public static void main(String[] args) {
long i = 10;
Long l = null;
if (i == l) {
System.out.print("fdfd");
}
}
} }

View File

@ -33,6 +33,7 @@ import com.cloud.exception.ManagementServerException;
import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.VirtualMachineMigrationException; import com.cloud.exception.VirtualMachineMigrationException;
import com.cloud.host.Host; import com.cloud.host.Host;
import com.cloud.storage.StoragePool;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.UserContext; import com.cloud.user.UserContext;
import com.cloud.uservm.UserVm; import com.cloud.uservm.UserVm;
@ -48,12 +49,14 @@ public class MigrateVMCmd extends BaseAsyncCmd {
//////////////// API parameters ///////////////////// //////////////// API parameters /////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, required=true, description="destination Host ID to migrate VM to") @Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, required=false, description="destination Host ID to migrate VM to")
private Long hostId; private Long hostId;
@Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.LONG, required=true, description="the ID of the virtual machine") @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.LONG, required=true, description="the ID of the virtual machine")
private Long virtualMachineId; private Long virtualMachineId;
@Parameter(name=ApiConstants.STORAGE_ID, type=CommandType.LONG, required=false, description="destination storage pool ID to migrate VM to")
private Long storageId;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////////// Accessors /////////////////////// /////////////////// Accessors ///////////////////////
@ -67,6 +70,10 @@ public class MigrateVMCmd extends BaseAsyncCmd {
return virtualMachineId; return virtualMachineId;
} }
public Long getStoragePoolId() {
return storageId;
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
@ -99,18 +106,44 @@ public class MigrateVMCmd extends BaseAsyncCmd {
@Override @Override
public void execute(){ public void execute(){
if (getHostId() == null && getStoragePoolId() == null) {
throw new InvalidParameterValueException("either hostId or storageId must be specified");
}
if (getHostId() != null && getStoragePoolId() != null) {
throw new InvalidParameterValueException("only one of hostId and storageId can be specified");
}
UserVm userVm = _userVmService.getUserVm(getVirtualMachineId()); UserVm userVm = _userVmService.getUserVm(getVirtualMachineId());
if (userVm == null) { if (userVm == null) {
throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId()); throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId());
} }
Host destinationHost = _resourceService.getHost(getHostId()); Host destinationHost = null;
if (getHostId() != null) {
destinationHost = _resourceService.getHost(getHostId());
if (destinationHost == null) { if (destinationHost == null) {
throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId()); throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId());
} }
try{
UserContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: "+ getHostId()); UserContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: "+ getHostId());
VirtualMachine migratedVm = _userVmService.migrateVirtualMachine(getVirtualMachineId(), destinationHost); }
StoragePool destStoragePool = null;
if (getStoragePoolId() != null) {
destStoragePool = _storageService.getStoragePool(getStoragePoolId());
if (destStoragePool == null) {
throw new InvalidParameterValueException("Unable to find the storage pool to migrate the VM");
}
UserContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to storage pool Id: "+ getStoragePoolId());
}
try{
VirtualMachine migratedVm = null;
if (getHostId() != null) {
migratedVm = _userVmService.migrateVirtualMachine(getVirtualMachineId(), destinationHost);
} else if (getStoragePoolId() != null) {
migratedVm = _userVmService.vmStorageMigration(getVirtualMachineId(), destStoragePool);
}
if (migratedVm != null) { if (migratedVm != null) {
UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", (UserVm)migratedVm).get(0); UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", (UserVm)migratedVm).get(0);
response.setResponseName(getCommandName()); response.setResponseName(getCommandName());

View File

@ -105,6 +105,7 @@ public class EventTypes {
public static final String EVENT_VOLUME_DETACH = "VOLUME.DETACH"; public static final String EVENT_VOLUME_DETACH = "VOLUME.DETACH";
public static final String EVENT_VOLUME_EXTRACT = "VOLUME.EXTRACT"; public static final String EVENT_VOLUME_EXTRACT = "VOLUME.EXTRACT";
public static final String EVENT_VOLUME_UPLOAD = "VOLUME.UPLOAD"; public static final String EVENT_VOLUME_UPLOAD = "VOLUME.UPLOAD";
public static final String EVENT_VOLUME_MIGRATE = "VOLUME.MIGRATE";
// Domains // Domains
public static final String EVENT_DOMAIN_CREATE = "DOMAIN.CREATE"; public static final String EVENT_DOMAIN_CREATE = "DOMAIN.CREATE";

View File

@ -18,6 +18,7 @@
package com.cloud.storage; package com.cloud.storage;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.List;
import com.cloud.api.commands.CancelPrimaryStorageMaintenanceCmd; import com.cloud.api.commands.CancelPrimaryStorageMaintenanceCmd;
import com.cloud.api.commands.CreateStoragePoolCmd; import com.cloud.api.commands.CreateStoragePoolCmd;
@ -105,4 +106,5 @@ public interface StorageService {
public StoragePool getStoragePool(long id); public StoragePool getStoragePool(long id);
Volume migrateVolume(Long volumeId, Long storagePoolId) throws ConcurrentOperationException;
} }

View File

@ -25,16 +25,22 @@ import com.cloud.acl.ControlledEntity;
import com.cloud.template.BasedOn; import com.cloud.template.BasedOn;
import com.cloud.utils.fsm.FiniteState; import com.cloud.utils.fsm.FiniteState;
import com.cloud.utils.fsm.StateMachine; import com.cloud.utils.fsm.StateMachine;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.utils.fsm.StateObject;
import com.cloud.vm.VirtualMachine;
public interface Volume extends ControlledEntity, BasedOn { public interface Volume extends ControlledEntity, BasedOn, StateObject<Volume.State> {
enum Type { enum Type {
UNKNOWN, ROOT, SWAP, DATADISK, ISO UNKNOWN, ROOT, SWAP, DATADISK, ISO
}; };
enum State implements FiniteState<State, Event> { enum State {
Allocated("The volume is allocated but has not been created yet."), Allocated("The volume is allocated but has not been created yet."),
Creating("The volume is being created. getPoolId() should reflect the pool where it is being created."), Creating("The volume is being created. getPoolId() should reflect the pool where it is being created."),
Ready("The volume is ready to be used."), Ready("The volume is ready to be used."),
Migrating("The volume is migrating to other storage pool"),
Snapshotting("There is a snapshot created on this volume, not backed up to secondary storage yet"),
Expunging("The volume is being expunging"),
Destroy("The volume is destroyed, and can't be recovered."); Destroy("The volume is destroyed, and can't be recovered.");
String _description; String _description;
@ -43,46 +49,44 @@ public interface Volume extends ControlledEntity, BasedOn {
_description = description; _description = description;
} }
@Override public static StateMachine2<State, Event, Volume> getStateMachine() {
public StateMachine<State, Event> getStateMachine() {
return s_fsm; return s_fsm;
} }
@Override
public State getNextState(Event event) {
return s_fsm.getNextState(this, event);
}
@Override
public List<State> getFromStates(Event event) {
return s_fsm.getFromStates(this, event);
}
@Override
public Set<Event> getPossibleEvents() {
return s_fsm.getPossibleEvents(this);
}
@Override
public String getDescription() { public String getDescription() {
return _description; return _description;
} }
private final static StateMachine<State, Event> s_fsm = new StateMachine<State, Event>(); private final static StateMachine2<State, Event, Volume> s_fsm = new StateMachine2<State, Event, Volume>();
static { static {
s_fsm.addTransition(Allocated, Event.Create, Creating); s_fsm.addTransition(Allocated, Event.CreateRequested, Creating);
s_fsm.addTransition(Allocated, Event.Destroy, Destroy); s_fsm.addTransition(Allocated, Event.DestroyRequested, Destroy);
s_fsm.addTransition(Creating, Event.OperationRetry, Creating); s_fsm.addTransition(Creating, Event.OperationRetry, Creating);
s_fsm.addTransition(Creating, Event.OperationFailed, Allocated); s_fsm.addTransition(Creating, Event.OperationFailed, Allocated);
s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready); s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Creating, Event.Destroy, Destroy); s_fsm.addTransition(Creating, Event.DestroyRequested, Destroy);
s_fsm.addTransition(Creating, Event.Create, Creating); s_fsm.addTransition(Creating, Event.CreateRequested, Creating);
s_fsm.addTransition(Ready, Event.Destroy, Destroy); s_fsm.addTransition(Ready, Event.DestroyRequested, Destroy);
s_fsm.addTransition(Destroy, Event.ExpungingRequested, Expunging);
s_fsm.addTransition(Ready, Event.SnapshotRequested, Snapshotting);
s_fsm.addTransition(Snapshotting, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Snapshotting, Event.OperationFailed, Ready);
s_fsm.addTransition(Ready, Event.MigrationRequested, Migrating);
s_fsm.addTransition(Migrating, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Migrating, Event.OperationFailed, Ready);
s_fsm.addTransition(Destroy, Event.OperationSucceeded, Destroy);
} }
} }
enum Event { enum Event {
Create, OperationFailed, OperationSucceeded, OperationRetry, Destroy; CreateRequested,
OperationFailed,
OperationSucceeded,
OperationRetry,
MigrationRequested,
SnapshotRequested,
DestroyRequested,
ExpungingRequested;
} }
long getId(); long getId();
@ -133,4 +137,10 @@ public interface Volume extends ControlledEntity, BasedOn {
String getChainInfo(); String getChainInfo();
boolean isRecreatable(); boolean isRecreatable();
public long getUpdatedCount();
public void incrUpdatedCount();
public Date getUpdated();
} }

View File

@ -48,6 +48,7 @@ import com.cloud.exception.VirtualMachineMigrationException;
import com.cloud.host.Host; import com.cloud.host.Host;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.offering.ServiceOffering; import com.cloud.offering.ServiceOffering;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account; import com.cloud.user.Account;
@ -358,11 +359,12 @@ public interface UserVmService {
/** /**
* Migrate the given VM to the destination host provided. The API returns the migrated VM if migration succeeds. Only Root * Migrate the given VM to the destination host provided. The API returns the migrated VM if migration succeeds. Only Root
* Admin can migrate a VM. * Admin can migrate a VM.
* * @param destinationStorage TODO
* @param Long vmId * @param Long vmId
* vmId of The VM to migrate * vmId of The VM to migrate
* @param Host * @param Host
* destinationHost to migrate the VM * destinationHost to migrate the VM
*
* @return VirtualMachine migrated VM * @return VirtualMachine migrated VM
* @throws ManagementServerException * @throws ManagementServerException
* in case we get error finding the VM or host or access errors or other internal errors. * in case we get error finding the VM or host or access errors or other internal errors.
@ -376,4 +378,6 @@ public interface UserVmService {
VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException;
UserVm moveVMToUser(MoveUserVMCmd moveUserVMCmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException ; UserVm moveVMToUser(MoveUserVMCmd moveUserVMCmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException ;
VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool);
} }

View File

@ -79,6 +79,7 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, StateObject
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.OperationFailed, State.Stopped); s_fsm.addTransition(State.Stopped, VirtualMachine.Event.OperationFailed, State.Stopped);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.ExpungeOperation, State.Expunging); s_fsm.addTransition(State.Stopped, VirtualMachine.Event.ExpungeOperation, State.Expunging);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.AgentReportShutdowned, State.Stopped); s_fsm.addTransition(State.Stopped, VirtualMachine.Event.AgentReportShutdowned, State.Stopped);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.StorageMigrationRequested, State.Migrating);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationRetry, State.Starting); s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationRetry, State.Starting);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationFailed, State.Stopped); s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationFailed, State.Stopped);
@ -160,6 +161,7 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, StateObject
AgentReportStopped, AgentReportStopped,
AgentReportRunning, AgentReportRunning,
MigrationRequested, MigrationRequested,
StorageMigrationRequested,
ExpungeOperation, ExpungeOperation,
OperationSucceeded, OperationSucceeded,
OperationFailed, OperationFailed,

View File

@ -50,6 +50,9 @@ public class VolumeVO implements Volume {
@Column(name = "pool_id") @Column(name = "pool_id")
Long poolId; Long poolId;
@Column(name = "last_pool_id")
Long lastPoolId;
@Column(name = "account_id") @Column(name = "account_id")
long accountId; long accountId;
@ -111,6 +114,9 @@ public class VolumeVO implements Volume {
@Temporal(value = TemporalType.TIMESTAMP) @Temporal(value = TemporalType.TIMESTAMP)
Date updated; Date updated;
@Column(name="update_count", updatable = true, nullable=false)
protected long updatedCount; // This field should be updated everytime the state is updated. There's no set method in the vo object because it is done with in the dao code.
@Column(name = "recreatable") @Column(name = "recreatable")
boolean recreatable; boolean recreatable;
@ -144,6 +150,7 @@ public class VolumeVO implements Volume {
this.podId = podId; this.podId = podId;
this.dataCenterId = dcId; this.dataCenterId = dcId;
this.volumeType = vType; this.volumeType = vType;
this.state = Volume.State.Allocated;
this.recreatable = false; this.recreatable = false;
} }
@ -162,6 +169,20 @@ public class VolumeVO implements Volume {
this.deviceId = that.getDeviceId(); this.deviceId = that.getDeviceId();
} }
@Override
public long getUpdatedCount() {
return this.updatedCount;
}
@Override
public void incrUpdatedCount() {
this.updatedCount++;
}
public void decrUpdatedCount() {
this.updatedCount--;
}
@Override @Override
public boolean isRecreatable() { public boolean isRecreatable() {
return recreatable; return recreatable;
@ -342,6 +363,7 @@ public class VolumeVO implements Volume {
this.poolId = poolId; this.poolId = poolId;
} }
@Override
public Date getUpdated() { public Date getUpdated() {
return updated; return updated;
} }
@ -351,10 +373,6 @@ public class VolumeVO implements Volume {
return state; return state;
} }
public void setState(State state) {
this.state = state;
}
public void setUpdated(Date updated) { public void setUpdated(Date updated) {
this.updated = updated; this.updated = updated;
} }
@ -382,6 +400,14 @@ public class VolumeVO implements Volume {
this.chainInfo = chainInfo; this.chainInfo = chainInfo;
} }
public Long getLastPoolId() {
return this.lastPoolId;
}
public void setLastPoolId(Long poolId) {
this.lastPoolId = poolId;
}
@Override @Override
public int hashCode() { public int hashCode() {
return NumbersUtil.hash(id); return NumbersUtil.hash(id);

View File

@ -2376,8 +2376,10 @@ public class ApiResponseHelper implements ResponseGenerator {
sgr.setDescription(sgd.getDescription()); sgr.setDescription(sgd.getDescription());
Account account = ApiDBUtils.findAccountByNameDomain(sgd.getAccountName(), sgd.getDomainId()); Account account = ApiDBUtils.findAccountByNameDomain(sgd.getAccountName(), sgd.getDomainId());
if (account != null) {
populateAccount(sgr, account.getId()); populateAccount(sgr, account.getId());
populateDomain(sgr, sgd.getDomainId()); populateDomain(sgr, sgd.getDomainId());
}
sgr.setObjectName(sgd.getObjectName()); sgr.setObjectName(sgd.getObjectName());
securityGroupResponse.add(sgr); securityGroupResponse.add(sgr);

View File

@ -510,12 +510,12 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet
} }
@Override @Override
public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Long id) { public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) {
return true; return true;
} }
@Override @Override
public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Long id) { public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) {
if (newState != State.Starting && newState != State.Error && newState != State.Expunging) { if (newState != State.Starting && newState != State.Error && newState != State.Expunging) {
return true; return true;
} }

View File

@ -50,6 +50,7 @@ import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil; import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.component.Inject; import com.cloud.utils.component.Inject;
import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB; import com.cloud.utils.db.DB;
@ -530,15 +531,18 @@ public class CapacityManagerImpl implements CapacityManager, StateListener<State
} }
@Override @Override
public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean transitionStatus, Long id) { public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean transitionStatus, Object opaque) {
return true; return true;
} }
@Override @Override
public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Long oldHostId) { public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Object opaque) {
if (!status) { if (!status) {
return false; return false;
} }
@SuppressWarnings("unchecked")
Pair<Long, Long> hosts = (Pair<Long, Long>)opaque;
Long oldHostId = hosts.first();
s_logger.debug("VM state transitted from :" + oldState + " to " + newState + " with event: " + event + "vm's original host id: " s_logger.debug("VM state transitted from :" + oldState + " to " + newState + " with event: " + event + "vm's original host id: "
+ vm.getLastHostId() + " new host id: " + vm.getHostId() + " host id before state transition: " + oldHostId); + vm.getLastHostId() + " new host id: " + vm.getHostId() + " host id before state transition: " + oldHostId);

View File

@ -110,7 +110,7 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager {
} }
@Override @Override
public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Long oldHostId) { public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Object opaque) {
if (!_isEnabled || !status || (vm.getType() != VirtualMachine.Type.User && vm.getType() != VirtualMachine.Type.DomainRouter)) { if (!_isEnabled || !status || (vm.getType() != VirtualMachine.Type.User && vm.getType() != VirtualMachine.Type.DomainRouter)) {
return false; return false;
} }
@ -123,7 +123,7 @@ public class OvsNetworkManagerImpl implements OvsNetworkManager {
} }
@Override @Override
public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Long id) { public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Object opaque) {
if (!_isEnabled || !status || (vm.getType() != VirtualMachine.Type.User && vm.getType() != VirtualMachine.Type.DomainRouter)) { if (!_isEnabled || !status || (vm.getType() != VirtualMachine.Type.User && vm.getType() != VirtualMachine.Type.DomainRouter)) {
return false; return false;
} }

View File

@ -1243,12 +1243,12 @@ public class SecurityGroupManagerImpl implements SecurityGroupManager, SecurityG
} }
@Override @Override
public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Long id) { public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) {
return true; return true;
} }
@Override @Override
public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Long oldHostId) { public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Object opaque) {
if (!status) { if (!status) {
return false; return false;
} }

View File

@ -33,10 +33,12 @@ import com.cloud.host.Host;
import com.cloud.host.HostVO; import com.cloud.host.HostVO;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.Volume.Event;
import com.cloud.storage.Volume.Type; import com.cloud.storage.Volume.Type;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.component.Manager; import com.cloud.utils.component.Manager;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.vm.DiskProfile; import com.cloud.vm.DiskProfile;
import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
@ -87,8 +89,9 @@ public interface StorageManager extends Manager {
* @param destPoolPodId * @param destPoolPodId
* @param destPoolClusterId * @param destPoolClusterId
* @return VolumeVO * @return VolumeVO
* @throws ConcurrentOperationException
*/ */
VolumeVO moveVolume(VolumeVO volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType); VolumeVO moveVolume(VolumeVO volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType) throws ConcurrentOperationException;
/** /**
* Create a volume based on the given criteria * Create a volume based on the given criteria
@ -111,8 +114,9 @@ public interface StorageManager extends Manager {
/** /**
* Marks the specified volume as destroyed in the management server database. The expunge thread will delete the volume from its storage pool. * Marks the specified volume as destroyed in the management server database. The expunge thread will delete the volume from its storage pool.
* @param volume * @param volume
* @return
*/ */
void destroyVolume(VolumeVO volume) throws ConcurrentOperationException; boolean destroyVolume(VolumeVO volume) throws ConcurrentOperationException;
/** Create capacity entries in the op capacity table /** Create capacity entries in the op capacity table
* @param storagePool * @param storagePool
@ -207,4 +211,11 @@ public interface StorageManager extends Manager {
VMTemplateHostVO getTemplateHostRef(long zoneId, long tmpltId, boolean readyOnly); VMTemplateHostVO getTemplateHostRef(long zoneId, long tmpltId, boolean readyOnly);
boolean StorageMigration(
VirtualMachineProfile<? extends VirtualMachine> vm,
StoragePool destPool) throws ConcurrentOperationException;
boolean stateTransitTo(Volume vol, Event event)
throws NoTransitionException;
} }

View File

@ -123,6 +123,7 @@ import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.Volume.Event;
import com.cloud.storage.Volume.Type; import com.cloud.storage.Volume.Type;
import com.cloud.storage.allocator.StoragePoolAllocator; import com.cloud.storage.allocator.StoragePoolAllocator;
import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.DiskOfferingDao;
@ -166,6 +167,8 @@ import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.Transaction; import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.exception.ExecutionException; import com.cloud.utils.exception.ExecutionException;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.ConsoleProxyVO;
import com.cloud.vm.DiskProfile; import com.cloud.vm.DiskProfile;
import com.cloud.vm.DomainRouterVO; import com.cloud.vm.DomainRouterVO;
@ -310,6 +313,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
protected float _overProvisioningFactor = 1; protected float _overProvisioningFactor = 1;
private long _maxVolumeSizeInGb; private long _maxVolumeSizeInGb;
private long _serverId; private long _serverId;
private StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
public boolean share(VMInstanceVO vm, List<VolumeVO> vols, HostVO host, boolean cancelPreviousShare) throws StorageUnavailableException { public boolean share(VMInstanceVO vm, List<VolumeVO> vols, HostVO host, boolean cancelPreviousShare) throws StorageUnavailableException {
@ -495,6 +499,12 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
String volumeFolder = null; String volumeFolder = null;
try {
stateTransitTo(volume, Volume.Event.CreateRequested);
} catch (NoTransitionException e) {
s_logger.debug(e.toString());
return null;
}
// Create the Volume object and save it so that we can return it to the user // Create the Volume object and save it so that we can return it to the user
Account account = _accountDao.findById(volume.getAccountId()); Account account = _accountDao.findById(volume.getAccountId());
@ -560,11 +570,10 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
throw new CloudRuntimeException(msg); throw new CloudRuntimeException(msg);
} }
// Update the volume in the database
Transaction txn = Transaction.currentTxn();
txn.start();
createdVolume = _volsDao.findById(volumeId); createdVolume = _volsDao.findById(volumeId);
try {
if (success) { if (success) {
createdVolume.setPodId(pod.first().getId()); createdVolume.setPodId(pod.first().getId());
createdVolume.setPoolId(pool.getId()); createdVolume.setPoolId(pool.getId());
@ -572,16 +581,21 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
createdVolume.setFolder(volumeFolder); createdVolume.setFolder(volumeFolder);
createdVolume.setPath(volumeUUID); createdVolume.setPath(volumeUUID);
createdVolume.setDomainId(account.getDomainId()); createdVolume.setDomainId(account.getDomainId());
createdVolume.setState(Volume.State.Ready); stateTransitTo(createdVolume, Volume.Event.OperationSucceeded);
} else { }
createdVolume.setState(Volume.State.Destroy); } catch (NoTransitionException e) {
s_logger.debug("Failed to update volume state: " + e.toString());
return null;
} }
_volsDao.update(volumeId, createdVolume);
txn.commit();
return new Pair<VolumeVO, String>(createdVolume, details); return new Pair<VolumeVO, String>(createdVolume, details);
} }
@Override
public boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException {
return _volStateMachine.transitTo(vol, event, null, _volsDao);
}
protected VolumeVO createVolumeFromSnapshot(VolumeVO volume, long snapshotId) { protected VolumeVO createVolumeFromSnapshot(VolumeVO volume, long snapshotId) {
// By default, assume failure. // By default, assume failure.
@ -589,7 +603,9 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
SnapshotVO snapshot = _snapshotDao.findById(snapshotId); // Precondition: snapshot is not null and not removed. SnapshotVO snapshot = _snapshotDao.findById(snapshotId); // Precondition: snapshot is not null and not removed.
Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(volume, snapshot); Pair<VolumeVO, String> volumeDetails = createVolumeFromSnapshot(volume, snapshot);
if (volumeDetails != null) {
createdVolume = volumeDetails.first(); createdVolume = volumeDetails.first();
}
return createdVolume; return createdVolume;
} }
@ -670,6 +686,13 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
StoragePoolVO pool = null; StoragePoolVO pool = null;
final HashSet<StoragePool> avoidPools = new HashSet<StoragePool>(avoids); final HashSet<StoragePool> avoidPools = new HashSet<StoragePool>(avoids);
try {
stateTransitTo(volume, Volume.Event.CreateRequested);
} catch (NoTransitionException e) {
s_logger.debug("Unable to update volume state: " + e.toString());
return null;
}
if (diskOffering != null && diskOffering.isCustomized()) { if (diskOffering != null && diskOffering.isCustomized()) {
diskOffering.setDiskSize(size); diskOffering.setDiskSize(size);
} }
@ -755,8 +778,13 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
volume.setPoolType(pool.getPoolType()); volume.setPoolType(pool.getPoolType());
volume.setPoolId(pool.getId()); volume.setPoolId(pool.getId());
volume.setPodId(pod.getId()); volume.setPodId(pod.getId());
volume.setState(Volume.State.Ready); try {
return _volsDao.persist(volume); stateTransitTo(volume, Volume.Event.OperationSucceeded);
} catch (NoTransitionException e) {
s_logger.debug("Unable to update volume state: " + e.toString());
return null;
}
return volume;
} }
} }
@ -1062,6 +1090,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
} }
protected StorageManagerImpl() { protected StorageManagerImpl() {
_volStateMachine = Volume.State.getStateMachine();
} }
@Override @Override
@ -1511,7 +1540,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
} }
@Override @Override
public VolumeVO moveVolume(VolumeVO volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType) { public VolumeVO moveVolume(VolumeVO volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType) throws ConcurrentOperationException {
List<SnapshotVO> snapshots = _snapshotDao.listByVolumeId(volume.getId()); List<SnapshotVO> snapshots = _snapshotDao.listByVolumeId(volume.getId());
if (snapshots != null && snapshots.size() > 0) { if (snapshots != null && snapshots.size() > 0) {
@ -1532,7 +1561,6 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
HostPodVO destPoolPod = _podDao.findById(destPoolPodId); HostPodVO destPoolPod = _podDao.findById(destPoolPodId);
StoragePoolVO destPool = findStoragePool(dskCh, destPoolDataCenter, destPoolPod, destPoolClusterId, null, new HashSet<StoragePool>()); StoragePoolVO destPool = findStoragePool(dskCh, destPoolDataCenter, destPoolPod, destPoolClusterId, null, new HashSet<StoragePool>());
String secondaryStorageURL = getSecondaryStorageURL(volume.getDataCenterId()); String secondaryStorageURL = getSecondaryStorageURL(volume.getDataCenterId());
String secondaryStorageVolumePath = null;
if (destPool == null) { if (destPool == null) {
throw new CloudRuntimeException("Failed to find a storage pool with enough capacity to move the volume to."); throw new CloudRuntimeException("Failed to find a storage pool with enough capacity to move the volume to.");
@ -1541,56 +1569,9 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
throw new CloudRuntimeException("Failed to find secondary storage."); throw new CloudRuntimeException("Failed to find secondary storage.");
} }
StoragePoolVO srcPool = _storagePoolDao.findById(volume.getPoolId()); List<Volume> vols = new ArrayList<Volume>();
vols.add(volume);
// Copy the volume from the source storage pool to secondary storage migrateVolumes(vols, destPool);
CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(), volume.getPath(), srcPool, secondaryStorageURL, true, _copyvolumewait);
CopyVolumeAnswer cvAnswer;
try {
cvAnswer = (CopyVolumeAnswer) sendToPool(srcPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException("Failed to copy the volume from the source primary storage pool to secondary storage.", e1);
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException("Failed to copy the volume from the source primary storage pool to secondary storage.");
}
secondaryStorageVolumePath = cvAnswer.getVolumePath();
// Copy the volume from secondary storage to the destination storage
// pool
cvCmd = new CopyVolumeCommand(volume.getId(), secondaryStorageVolumePath, destPool, secondaryStorageURL, false, _copyvolumewait);
try {
cvAnswer = (CopyVolumeAnswer) sendToPool(destPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
String destPrimaryStorageVolumePath = cvAnswer.getVolumePath();
String destPrimaryStorageVolumeFolder = cvAnswer.getVolumeFolder();
// Delete the volume on the source storage pool
final DestroyCommand cmd = new DestroyCommand(srcPool, volume, null);
volume.setPath(destPrimaryStorageVolumePath);
volume.setFolder(destPrimaryStorageVolumeFolder);
volume.setPodId(destPool.getPodId());
volume.setPoolId(destPool.getId());
_volsDao.update(volume.getId(), volume);
Answer destroyAnswer = null;
try {
destroyAnswer = sendToPool(srcPool, cmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException("Failed to destroy the volume from the source primary storage pool to secondary storage.");
}
if (destroyAnswer == null || !destroyAnswer.getResult()) {
throw new CloudRuntimeException("Failed to destroy the volume from the source primary storage pool to secondary storage.");
}
return _volsDao.findById(volume.getId()); return _volsDao.findById(volume.getId());
} }
@ -1730,11 +1711,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
volume.setInstanceId(null); volume.setInstanceId(null);
volume.setUpdated(new Date()); volume.setUpdated(new Date());
volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId()); volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId());
if (cmd.getSnapshotId() == null) {
volume.setState(Volume.State.Allocated);
} else {
volume.setState(Volume.State.Creating);
}
volume = _volsDao.persist(volume); volume = _volsDao.persist(volume);
UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size); UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size);
_usageEventDao.persist(usageEvent); _usageEventDao.persist(usageEvent);
@ -1779,12 +1756,16 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
@Override @Override
@DB @DB
public void destroyVolume(VolumeVO volume) throws ConcurrentOperationException { public boolean destroyVolume(VolumeVO volume) throws ConcurrentOperationException {
assert (volume.getState() != Volume.State.Destroy) : "Why destroy method is called for the volume that is already destroyed?"; try {
Transaction txn = Transaction.currentTxn(); if (!stateTransitTo(volume, Volume.Event.DestroyRequested)) {
txn.start(); throw new ConcurrentOperationException("Failed to transit to destroyed state");
}
} catch (NoTransitionException e) {
s_logger.debug("Unable to destoy the volume: " + e.toString());
return false;
}
_volsDao.update(volume, Volume.Event.Destroy);
long volumeId = volume.getId(); long volumeId = volume.getId();
// Delete the recurring snapshot policies for this volume. // Delete the recurring snapshot policies for this volume.
@ -1804,7 +1785,18 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
_usageEventDao.persist(usageEvent); _usageEventDao.persist(usageEvent);
} }
txn.commit(); try {
if (!stateTransitTo(volume, Volume.Event.OperationSucceeded)) {
throw new ConcurrentOperationException("Failed to transit state");
}
} catch (NoTransitionException e) {
s_logger.debug("Unable to change volume state: " + e.toString());
return false;
}
return true;
} }
@Override @Override
@ -2406,7 +2398,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
Account caller = UserContext.current().getCaller(); Account caller = UserContext.current().getCaller();
// Check that the volume ID is valid // Check that the volume ID is valid
VolumeVO volume = _volsDao.acquireInLockTable(volumeId, 10); VolumeVO volume = _volsDao.findById(volumeId);
if (volume == null) { if (volume == null) {
throw new InvalidParameterValueException("Unable to aquire volume with ID: " + volumeId); throw new InvalidParameterValueException("Unable to aquire volume with ID: " + volumeId);
} }
@ -2414,18 +2406,6 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
//permission check //permission check
_accountMgr.checkAccess(caller, null, volume); _accountMgr.checkAccess(caller, null, volume);
try {
// Check that the volume is stored on shared storage
// NOTE: We used to ensure the volume is on shared storage before deleting. However, this seems like an unnecessary
// check since all we allow
// is deleting a detached volume. Is there a technical reason why the volume has to be on shared storage? If so,
// uncomment this...otherwise,
// just delete the detached volume regardless of storage pool.
// if (!volumeOnSharedStoragePool(volume)) {
// throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool.");
// }
// Check that the volume is not currently attached to any VM // Check that the volume is not currently attached to any VM
if (volume.getInstanceId() != null) { if (volume.getInstanceId() != null) {
throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM."); throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM.");
@ -2433,7 +2413,9 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
// Check that the volume is not already destroyed // Check that the volume is not already destroyed
if (volume.getState() != Volume.State.Destroy) { if (volume.getState() != Volume.State.Destroy) {
destroyVolume(volume); if (!destroyVolume(volume)) {
return false;
}
} }
try { try {
@ -2442,9 +2424,6 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
s_logger.warn("Failed to expunge volume:", e); s_logger.warn("Failed to expunge volume:", e);
return false; return false;
} }
} finally {
_volumeDao.releaseFromLockTable(volumeId);
}
return true; return true;
} }
@ -2570,6 +2549,189 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
} }
} }
@DB
@Override
public Volume migrateVolume(Long volumeId, Long storagePoolId) throws ConcurrentOperationException {
VolumeVO vol = _volsDao.findById(volumeId);
if (vol == null) {
throw new InvalidParameterValueException("Failed to find the volume id: " + volumeId);
}
if (vol.getState() != Volume.State.Ready) {
throw new InvalidParameterValueException("Volume must be in ready state");
}
if (vol.getInstanceId() != null) {
throw new InvalidParameterValueException("Volume needs to be dettached from VM");
}
StoragePool destPool = _storagePoolDao.findById(storagePoolId);
if (destPool == null) {
throw new InvalidParameterValueException("Faild to find the destination storage pool: " + storagePoolId);
}
List<Volume> vols = new ArrayList<Volume>();
vols.add(vol);
migrateVolumes(vols, destPool);
return vol;
}
@DB
public boolean migrateVolumes(List<Volume> volumes, StoragePool destPool) throws ConcurrentOperationException {
Transaction txn = Transaction.currentTxn();
txn.start();
boolean transitResult = false;
try {
for (Volume volume : volumes) {
try {
if (!stateTransitTo(volume, Volume.Event.MigrationRequested)) {
throw new ConcurrentOperationException("Failed to transit volume state");
}
} catch (NoTransitionException e) {
s_logger.debug("Failed to set state into migrate: " + e.toString());
throw new CloudRuntimeException("Failed to set state into migrate: " + e.toString());
}
}
transitResult = true;
} finally {
if (!transitResult) {
txn.rollback();
} else {
txn.commit();
}
}
//At this stage, nobody can modify volumes. Send the copyvolume command
List<Pair<StoragePoolVO,DestroyCommand>> destroyCmds = new ArrayList<Pair<StoragePoolVO,DestroyCommand>>();
List<CopyVolumeAnswer> answers = new ArrayList<CopyVolumeAnswer>();
try {
for (Volume volume: volumes) {
String secondaryStorageURL = getSecondaryStorageURL(volume.getDataCenterId());
StoragePoolVO srcPool = _storagePoolDao.findById(volume.getPoolId());
CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(), volume.getPath(), srcPool, secondaryStorageURL, true, _copyvolumewait);
CopyVolumeAnswer cvAnswer;
try {
cvAnswer = (CopyVolumeAnswer) sendToPool(srcPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException("Failed to copy the volume from the source primary storage pool to secondary storage.", e1);
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException("Failed to copy the volume from the source primary storage pool to secondary storage.");
}
String secondaryStorageVolumePath = cvAnswer.getVolumePath();
// Copy the volume from secondary storage to the destination storage
// pool
cvCmd = new CopyVolumeCommand(volume.getId(), secondaryStorageVolumePath, destPool, secondaryStorageURL, false, _copyvolumewait);
try {
cvAnswer = (CopyVolumeAnswer) sendToPool(destPool, cvCmd);
} catch (StorageUnavailableException e1) {
throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
if (cvAnswer == null || !cvAnswer.getResult()) {
throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool.");
}
answers.add(cvAnswer);
destroyCmds.add(new Pair<StoragePoolVO, DestroyCommand>(srcPool, new DestroyCommand(srcPool, volume, null)));
}
} finally {
if (answers.size() != volumes.size()) {
//this means one of copying volume failed
for (Volume volume : volumes) {
try {
stateTransitTo(volume, Volume.Event.OperationFailed);
} catch (NoTransitionException e) {
s_logger.debug("Failed to change volume state: " + e.toString());
}
}
} else {
//Need a transaction, make sure all the volumes get migrated to new storage pool
txn = Transaction.currentTxn();
txn.start();
transitResult = false;
try {
for (int i = 0; i < volumes.size(); i++) {
CopyVolumeAnswer answer = answers.get(i);
VolumeVO volume = (VolumeVO)volumes.get(i);
Long oldPoolId = volume.getPoolId();
volume.setPath(answer.getVolumePath());
volume.setFolder(answer.getVolumeFolder());
volume.setPodId(destPool.getPodId());
volume.setPoolId(destPool.getId());
volume.setLastPoolId(oldPoolId);
try {
stateTransitTo(volume, Volume.Event.OperationSucceeded);
} catch (NoTransitionException e) {
s_logger.debug("Failed to change volume state: " + e.toString());
throw new CloudRuntimeException("Failed to change volume state: " + e.toString());
}
}
transitResult = true;
} finally {
if (!transitResult) {
txn.rollback();
} else {
txn.commit();
}
}
}
}
//all the volumes get migrated to new storage pool, need to delete the copy on old storage pool
for (Pair<StoragePoolVO, DestroyCommand> cmd : destroyCmds) {
try {
Answer cvAnswer = sendToPool(cmd.first(), cmd.second());
} catch (StorageUnavailableException e) {
s_logger.debug("Unable to delete the old copy on storage pool: " + e.toString());
}
}
return true;
}
@Override
public boolean StorageMigration(VirtualMachineProfile<? extends VirtualMachine> vm, StoragePool destPool) throws ConcurrentOperationException {
List<VolumeVO> vols = _volsDao.findUsableVolumesForInstance(vm.getId());
List<Volume> volumesNeedToMigrate = new ArrayList<Volume>();
for (VolumeVO volume : vols) {
if (volume.getState() != Volume.State.Ready) {
s_logger.debug("volume: " + volume.getId() + " is in " + volume.getState() + " state");
throw new CloudRuntimeException("volume: " + volume.getId() + " is in " + volume.getState() + " state");
}
if (volume.getPoolId() == destPool.getId()) {
s_logger.debug("volume: " + volume.getId() + " is on the same storage pool: " + destPool.getId());
continue;
}
//Only support migrate in the same pod at first.
if (volume.getPodId() != destPool.getPodId()) {
throw new InvalidParameterValueException("Can't migrate vm between different pods: volume: " + volume.getId() + " at pod: "
+ volume.getPodId() + ", while dest pool: " + destPool.getId() + " at pod: " + destPool.getPodId());
}
volumesNeedToMigrate.add(volume);
}
if (volumesNeedToMigrate.isEmpty()) {
s_logger.debug("No volume need to be migrated");
return true;
}
return migrateVolumes(volumesNeedToMigrate, destPool);
}
@Override @Override
public void prepare(VirtualMachineProfile<? extends VirtualMachine> vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException { public void prepare(VirtualMachineProfile<? extends VirtualMachine> vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException {
@ -2639,17 +2801,17 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
} }
try { try {
_volsDao.update(newVol, Volume.Event.Create); stateTransitTo(newVol, Volume.Event.CreateRequested);
} catch (ConcurrentOperationException e) { } catch (NoTransitionException e) {
throw new StorageUnavailableException("Unable to create " + newVol, newVol.getPoolId()); throw new CloudRuntimeException("Unable to create " + e.toString());
} }
Pair<VolumeTO, StoragePool> created = createVolume(newVol, _diskOfferingDao.findById(newVol.getDiskOfferingId()), vm, vols, dest); Pair<VolumeTO, StoragePool> created = createVolume(newVol, _diskOfferingDao.findById(newVol.getDiskOfferingId()), vm, vols, dest);
if (created == null) { if (created == null) {
Long poolId = newVol.getPoolId(); Long poolId = newVol.getPoolId();
newVol.setPoolId(null); newVol.setPoolId(null);
try { try {
_volsDao.update(newVol, Volume.Event.OperationFailed); stateTransitTo(newVol, Volume.Event.OperationFailed);
} catch (ConcurrentOperationException e) { } catch (NoTransitionException e) {
throw new CloudRuntimeException("Unable to update the failure on a volume: " + newVol, e); throw new CloudRuntimeException("Unable to update the failure on a volume: " + newVol, e);
} }
throw new StorageUnavailableException("Unable to create " + newVol, poolId == null ? -1L : poolId); throw new StorageUnavailableException("Unable to create " + newVol, poolId == null ? -1L : poolId);
@ -2661,8 +2823,8 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
newVol.setPoolType(created.second().getPoolType()); newVol.setPoolType(created.second().getPoolType());
newVol.setPodId(created.second().getPodId()); newVol.setPodId(created.second().getPodId());
try { try {
_volsDao.update(newVol, Volume.Event.OperationSucceeded); stateTransitTo(newVol, Volume.Event.OperationSucceeded);
} catch (ConcurrentOperationException e) { } catch (NoTransitionException e) {
throw new CloudRuntimeException("Unable to update an CREATE operation succeeded on volume " + newVol, e); throw new CloudRuntimeException("Unable to update an CREATE operation succeeded on volume " + newVol, e);
} }
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
@ -2676,9 +2838,12 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
@DB @DB
protected VolumeVO switchVolume(VolumeVO existingVolume, VirtualMachineProfile<? extends VirtualMachine> vm) throws StorageUnavailableException { protected VolumeVO switchVolume(VolumeVO existingVolume, VirtualMachineProfile<? extends VirtualMachine> vm) throws StorageUnavailableException {
Transaction txn = Transaction.currentTxn(); Transaction txn = Transaction.currentTxn();
try {
txn.start(); txn.start();
_volsDao.update(existingVolume, Volume.Event.Destroy); try {
stateTransitTo(existingVolume, Volume.Event.DestroyRequested);
} catch (NoTransitionException e) {
s_logger.debug("Unable to destroy existing volume: " + e.toString());
}
Long templateIdToUse = null; Long templateIdToUse = null;
Long volTemplateId = existingVolume.getTemplateId(); Long volTemplateId = existingVolume.getTemplateId();
@ -2692,9 +2857,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
VolumeVO newVolume = allocateDuplicateVolume(existingVolume, templateIdToUse); VolumeVO newVolume = allocateDuplicateVolume(existingVolume, templateIdToUse);
txn.commit(); txn.commit();
return newVolume; return newVolume;
} catch (ConcurrentOperationException e) {
throw new StorageUnavailableException("Unable to duplicate the volume " + existingVolume, existingVolume.getPoolId(), e);
}
} }
public Pair<VolumeTO, StoragePool> createVolume(VolumeVO toBeCreated, DiskOfferingVO offering, VirtualMachineProfile<? extends VirtualMachine> vm, List<? extends Volume> alreadyCreated, public Pair<VolumeTO, StoragePool> createVolume(VolumeVO toBeCreated, DiskOfferingVO offering, VirtualMachineProfile<? extends VirtualMachine> vm, List<? extends Volume> alreadyCreated,
@ -2716,8 +2879,8 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
} }
toBeCreated.setPoolId(pool.getId()); toBeCreated.setPoolId(pool.getId());
try { try {
_volsDao.update(toBeCreated, Volume.Event.OperationRetry); stateTransitTo(toBeCreated, Volume.Event.OperationRetry);
} catch (ConcurrentOperationException e) { } catch (NoTransitionException e) {
throw new CloudRuntimeException("Unable to retry a create operation on volume " + toBeCreated); throw new CloudRuntimeException("Unable to retry a create operation on volume " + toBeCreated);
} }
@ -2958,6 +3121,8 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
return capacities; return capacities;
} }
@Override @Override
public StoragePool getStoragePool(long id) { public StoragePool getStoragePool(long id) {

View File

@ -26,8 +26,9 @@ import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO; import com.cloud.storage.VolumeVO;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDao;
import com.cloud.utils.fsm.StateDao;
public interface VolumeDao extends GenericDao<VolumeVO, Long> { public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.State, Volume.Event, Volume> {
List<VolumeVO> findDetachedByAccount(long accountId); List<VolumeVO> findDetachedByAccount(long accountId);
List<VolumeVO> findByAccount(long accountId); List<VolumeVO> findByAccount(long accountId);
Pair<Long, Long> getCountAndTotalByPool(long poolId); Pair<Long, Long> getCountAndTotalByPool(long poolId);
@ -45,13 +46,7 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long> {
List<VolumeVO> findByInstanceAndDeviceId(long instanceId, long deviceId); List<VolumeVO> findByInstanceAndDeviceId(long instanceId, long deviceId);
List<VolumeVO> findUsableVolumesForInstance(long instanceId); List<VolumeVO> findUsableVolumesForInstance(long instanceId);
Long countAllocatedVolumesForAccount(long accountId); Long countAllocatedVolumesForAccount(long accountId);
/**
* Updates the volume only if the state in memory matches the state in the database.
* @param vol Volume to be updated.
* @param event event that causes the database change.
* @return true if update happened, false if not.
*/
boolean update(VolumeVO vol, Volume.Event event) throws ConcurrentOperationException;
HypervisorType getHypervisorType(long volumeId); HypervisorType getHypervisorType(long volumeId);
List<VolumeVO> listVolumesToBeDestroyed(); List<VolumeVO> listVolumesToBeDestroyed();

View File

@ -31,6 +31,7 @@ import com.cloud.exception.ConcurrentOperationException;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
import com.cloud.storage.Volume.Event;
import com.cloud.storage.Volume.Type; import com.cloud.storage.Volume.Type;
import com.cloud.storage.VolumeVO; import com.cloud.storage.VolumeVO;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
@ -58,7 +59,6 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
protected final SearchBuilder<VolumeVO> InstanceStatesSearch; protected final SearchBuilder<VolumeVO> InstanceStatesSearch;
protected final SearchBuilder<VolumeVO> AllFieldsSearch; protected final SearchBuilder<VolumeVO> AllFieldsSearch;
protected GenericSearchBuilder<VolumeVO, Long> CountByAccount; protected GenericSearchBuilder<VolumeVO, Long> 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_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 = ?"; 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 = ?";
@ -203,28 +203,6 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
update(volumeId, volume); 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<VolumeVO> 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 @Override
@DB @DB
public HypervisorType getHypervisorType(long volumeId) { public HypervisorType getHypervisorType(long volumeId) {
@ -275,6 +253,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ);
AllFieldsSearch.and("destroyed", AllFieldsSearch.entity().getState(), Op.EQ); AllFieldsSearch.and("destroyed", AllFieldsSearch.entity().getState(), Op.EQ);
AllFieldsSearch.and("notDestroyed", AllFieldsSearch.entity().getState(), Op.NEQ); AllFieldsSearch.and("notDestroyed", AllFieldsSearch.entity().getState(), Op.NEQ);
AllFieldsSearch.and("updatedCount", AllFieldsSearch.entity().getUpdatedCount(), Op.EQ);
AllFieldsSearch.done(); AllFieldsSearch.done();
DetachedAccountIdSearch = createSearchBuilder(); DetachedAccountIdSearch = createSearchBuilder();
@ -312,9 +291,6 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountByAccount.done(); CountByAccount.done();
_stateAttr = _allAttributes.get("state");
assert _stateAttr != null : "Couldn't get the state attribute";
} }
@Override @DB(txn=false) @Override @DB(txn=false)
@ -348,4 +324,38 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
return listBy(sc); return listBy(sc);
} }
@Override
public boolean updateState(com.cloud.storage.Volume.State currentState,
Event event, com.cloud.storage.Volume.State nextState, Volume vo,
Object data) {
Long oldUpdated = vo.getUpdatedCount();
Date oldUpdatedTime = vo.getUpdated();
SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
sc.setParameters("id", vo.getId());
sc.setParameters("state", currentState);
sc.setParameters("updatedCount", vo.getUpdatedCount());
vo.incrUpdatedCount();
UpdateBuilder builder = getUpdateBuilder(vo);
builder.set(vo, "state", nextState);
builder.set(vo, "updated", new Date());
int rows = update((VolumeVO)vo, sc);
if (rows == 0 && s_logger.isDebugEnabled()) {
VolumeVO dbVol = findByIdIncludingRemoved(vo.getId());
if (dbVol != null) {
StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString());
str.append(": DB Data={id=").append(dbVol.getId()).append("; state=").append(dbVol.getState()).append("; updatecount=").append(dbVol.getUpdatedCount()).append(";updatedTime=").append(dbVol.getUpdated());
str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatecount=").append(vo.getUpdatedCount()).append("; updatedTime=").append(vo.getUpdated());
str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatecount=").append(oldUpdated).append("; updatedTime=").append(oldUpdatedTime);
} else {
s_logger.debug("Unable to update volume: id=" + vo.getId() + ", as there is no such volume exists in the database anymore");
}
}
return rows > 0;
}
} }

View File

@ -112,6 +112,7 @@ import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction; import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.vm.UserVmVO; import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachine.State;
@ -292,6 +293,7 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
_snapshotDao.update(snapshotId, snapshot); _snapshotDao.update(snapshotId, snapshot);
} else { } else {
long preSnapshotId = 0; long preSnapshotId = 0;
if (preSnapshotVO != null && preSnapshotVO.getBackupSnapshotId() != null) { if (preSnapshotVO != null && preSnapshotVO.getBackupSnapshotId() != null) {
preSnapshotId = preId; preSnapshotId = preId;
// default delta snap number is 16 // default delta snap number is 16
@ -315,6 +317,12 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
preSnapshotId = 0; preSnapshotId = 0;
} }
} }
//If the volume is moved around, backup a full snapshot to secondary storage
if (volume.getLastPoolId() != null && volume.getPoolId() != volume.getLastPoolId()) {
preSnapshotId = 0;
volume.setLastPoolId(volume.getPoolId());
}
snapshot = updateDBOnCreate(snapshotId, answer.getSnapshotPath(), preSnapshotId); snapshot = updateDBOnCreate(snapshotId, answer.getSnapshotPath(), preSnapshotId);
} }
// Get the snapshot_schedule table entry for this snapshot and // Get the snapshot_schedule table entry for this snapshot and
@ -348,17 +356,24 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
@DB @DB
@ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "creating snapshot", async = true) @ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "creating snapshot", async = true)
public SnapshotVO createSnapshot(Long volumeId, Long policyId, Long snapshotId) { public SnapshotVO createSnapshot(Long volumeId, Long policyId, Long snapshotId) {
VolumeVO v = _volsDao.findById(volumeId); VolumeVO volume = _volsDao.findById(volumeId);
Account owner = _accountMgr.getAccount(v.getAccountId());
if (volume == null) {
throw new InvalidParameterValueException("No such volume exist");
}
Account owner = _accountMgr.getAccount(volume.getAccountId());
SnapshotVO snapshot = null; SnapshotVO snapshot = null;
VolumeVO volume = null;
boolean backedUp = false; boolean backedUp = false;
// does the caller have the authority to act on this volume // does the caller have the authority to act on this volume
_accountMgr.checkAccess(UserContext.current().getCaller(), null, v); _accountMgr.checkAccess(UserContext.current().getCaller(), null, volume);
try { try {
if (v != null && _volsDao.getHypervisorType(v.getId()).equals(HypervisorType.KVM)) { if (volume != null && _volsDao.getHypervisorType(volume.getId()).equals(HypervisorType.KVM)) {
/* KVM needs to lock on the vm of volume, because it takes snapshot on behalf of vm, not volume */ /* KVM needs to lock on the vm of volume, because it takes snapshot on behalf of vm, not volume */
UserVmVO uservm = _vmDao.findById(v.getInstanceId()); UserVmVO uservm = _vmDao.findById(volume.getInstanceId());
if (uservm != null) { if (uservm != null) {
UserVmVO vm = _vmDao.acquireInLockTable(uservm.getId(), 10); UserVmVO vm = _vmDao.acquireInLockTable(uservm.getId(), 10);
if (vm == null) { if (vm == null) {
@ -366,13 +381,13 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
} }
} }
} }
Long poolId = v.getPoolId(); Long poolId = volume.getPoolId();
if (poolId == null) { if (poolId == null) {
throw new CloudRuntimeException("You cannot take a snapshot of a volume until it has been attached to an instance"); throw new CloudRuntimeException("You cannot take a snapshot of a volume until it has been attached to an instance");
} }
if (_volsDao.getHypervisorType(v.getId()).equals(HypervisorType.KVM)) { if (_volsDao.getHypervisorType(volume.getId()).equals(HypervisorType.KVM)) {
StoragePoolVO storagePool = _storagePoolDao.findById(v.getPoolId()); StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId());
ClusterVO cluster = _clusterDao.findById(storagePool.getClusterId()); ClusterVO cluster = _clusterDao.findById(storagePool.getClusterId());
List<HostVO> hosts = _hostDao.listByCluster(cluster.getId()); List<HostVO> hosts = _hostDao.listByCluster(cluster.getId());
if (hosts != null && !hosts.isEmpty()) { if (hosts != null && !hosts.isEmpty()) {
@ -385,8 +400,8 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
} }
// if volume is attached to a vm in destroyed or expunging state; disallow // if volume is attached to a vm in destroyed or expunging state; disallow
if (v.getInstanceId() != null) { if (volume.getInstanceId() != null) {
UserVmVO userVm = _vmDao.findById(v.getInstanceId()); UserVmVO userVm = _vmDao.findById(volume.getInstanceId());
if (userVm != null) { if (userVm != null) {
if (userVm.getState().equals(State.Destroyed) || userVm.getState().equals(State.Expunging)) { if (userVm.getState().equals(State.Destroyed) || userVm.getState().equals(State.Expunging)) {
_snapshotDao.expunge(snapshotId); _snapshotDao.expunge(snapshotId);
@ -395,21 +410,22 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
} }
if(userVm.getHypervisorType() == HypervisorType.VMware) { if(userVm.getHypervisorType() == HypervisorType.VMware) {
List<SnapshotVO> activeSnapshots = _snapshotDao.listByInstanceId(v.getInstanceId(), Snapshot.Status.Creating, Snapshot.Status.CreatedOnPrimary, Snapshot.Status.BackingUp); List<SnapshotVO> activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.Status.Creating, Snapshot.Status.CreatedOnPrimary, Snapshot.Status.BackingUp);
if(activeSnapshots.size() > 1) if(activeSnapshots.size() > 1)
throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later"); throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later");
} }
} }
} }
volume = _volsDao.acquireInLockTable(volumeId, 10); //when taking snapshot, make sure nobody can delete/move the volume
if (volume == null) { boolean stateTransit = false;
try {
stateTransit = _storageMgr.stateTransitTo(volume, Volume.Event.SnapshotRequested);
} catch (NoTransitionException e) {
s_logger.debug("Failed transit volume state: " + e.toString());
} finally {
if (!stateTransit) {
_snapshotDao.expunge(snapshotId); _snapshotDao.expunge(snapshotId);
volume = _volsDao.findById(volumeId);
if (volume == null) {
throw new CloudRuntimeException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist");
} else {
volume = null;
throw new CloudRuntimeException("Creating snapshot failed due to volume:" + volumeId + " is being used, try it later "); throw new CloudRuntimeException("Creating snapshot failed due to volume:" + volumeId + " is being used, try it later ");
} }
} }
@ -442,7 +458,7 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
SnapshotVO freshSnapshot = _snapshotDao.findById(snapshot.getId()); SnapshotVO freshSnapshot = _snapshotDao.findById(snapshot.getId());
if ((freshSnapshot != null) && backedUp) { if ((freshSnapshot != null) && backedUp) {
UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null,
v.getSize()); volume.getSize());
_usageEventDao.persist(usageEvent); _usageEventDao.persist(usageEvent);
} }
if( !backedUp ) { if( !backedUp ) {
@ -460,9 +476,12 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
} }
} }
if ( volume != null ) { try {
_volsDao.releaseFromLockTable(volumeId); _storageMgr.stateTransitTo(volume, Volume.Event.OperationSucceeded);
} catch (NoTransitionException e) {
s_logger.debug("Failed to transit volume state: " + e.toString());
} }
} }
return snapshot; return snapshot;

View File

@ -156,6 +156,7 @@ import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolStatus;
import com.cloud.storage.StoragePoolVO; import com.cloud.storage.StoragePoolVO;
import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateHostVO;
@ -650,7 +651,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
if (moveVolumeNeeded) { if (moveVolumeNeeded) {
// Move the volume to a storage pool in the VM's zone, pod, or cluster // Move the volume to a storage pool in the VM's zone, pod, or cluster
try {
volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType); volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType);
} catch (ConcurrentOperationException e) {
throw new CloudRuntimeException(e.toString());
}
} }
AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor();
@ -3187,6 +3192,39 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
return _vmDao.findById(vmId); return _vmDao.findById(vmId);
} }
@Override
public VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool) {
// access check - only root admin can migrate VM
Account caller = UserContext.current().getCaller();
if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Caller is not a root admin, permission denied to migrate the VM");
}
throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!");
}
VMInstanceVO vm = _vmInstanceDao.findById(vmId);
if (vm == null) {
throw new InvalidParameterValueException("Unable to find the VM by id=" + vmId);
}
if (vm.getState() != State.Stopped) {
throw new InvalidParameterValueException("VM is not Stopped, unable to migrate the vm " + vm);
}
if (vm.getType() != VirtualMachine.Type.User) {
throw new InvalidParameterValueException("can only do storage migration on user vm");
}
HypervisorType destHypervisorType = _clusterDao.findById(destPool.getClusterId()).getHypervisorType();
if (vm.getHypervisorType() != destHypervisorType) {
throw new InvalidParameterValueException("hypervisor is not compatible: dest: " + destHypervisorType.toString() + ", vm: " + vm.getHypervisorType().toString());
}
VMInstanceVO migratedVm = _itMgr.storageMigration(vm, destPool);
return migratedVm;
}
@Override @Override
@ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true)
public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {

View File

@ -42,12 +42,12 @@ public class UserVmStateListener implements StateListener<State, VirtualMachine.
} }
@Override @Override
public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Long id) { public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) {
return true; return true;
} }
@Override @Override
public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Long id) { public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) {
if(!status){ if(!status){
return false; return false;
} }

View File

@ -35,6 +35,7 @@ import com.cloud.network.NetworkVO;
import com.cloud.offering.ServiceOffering; import com.cloud.offering.ServiceOffering;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.StoragePool;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.User; import com.cloud.user.User;
@ -122,4 +123,6 @@ public interface VirtualMachineManager extends Manager {
VMInstanceVO findById(long vmId); VMInstanceVO findById(long vmId);
<T extends VMInstanceVO> T storageMigration(T vm, StoragePool storagePoolId);
} }

View File

@ -109,6 +109,7 @@ import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolVO; import com.cloud.storage.StoragePoolVO;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
@ -1104,7 +1105,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
protected boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId, String reservationId) throws NoTransitionException { protected boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId, String reservationId) throws NoTransitionException {
vm.setReservationId(reservationId); vm.setReservationId(reservationId);
return _stateMachine.transitTo(vm, e, hostId, _vmDao); return _stateMachine.transitTo(vm, e, new Pair<Long, Long>(vm.getHostId(), hostId), _vmDao);
} }
@Override @Override
@ -1119,7 +1120,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
vm.setLastHostId(vm.getHostId()); vm.setLastHostId(vm.getHostId());
} }
} }
return _stateMachine.transitTo(vm, e, hostId, _vmDao); return _stateMachine.transitTo(vm, e, new Pair<Long, Long>(vm.getHostId(), hostId), _vmDao);
} }
@Override @Override
@ -1170,6 +1171,52 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
return true; return true;
} }
@Override
public <T extends VMInstanceVO> T storageMigration(T vm, StoragePool destPool) {
VirtualMachineGuru<T> vmGuru = getVmGuru(vm);
long vmId = vm.getId();
vm = vmGuru.findById(vmId);
try {
stateTransitTo(vm, VirtualMachine.Event.StorageMigrationRequested, null);
} catch (NoTransitionException e) {
s_logger.debug("Unable to migrate vm: " + e.toString());
throw new CloudRuntimeException("Unable to migrate vm: " + e.toString());
}
VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
boolean migrationResult = false;
try {
try {
migrationResult = _storageMgr.StorageMigration(profile, destPool);
} catch (ConcurrentOperationException e) {
}
} finally {
if (migrationResult) {
try {
//when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool
vm.setLastHostId(null);
stateTransitTo(vm, VirtualMachine.Event.AgentReportStopped, null);
} catch (NoTransitionException e) {
s_logger.debug("Failed to migrate vm: " + e.toString());
throw new CloudRuntimeException("Failed to migrate vm: " + e.toString());
}
} else {
s_logger.debug("storage migration failed: ");
try {
stateTransitTo(vm, VirtualMachine.Event.AgentReportStopped, null);
} catch (NoTransitionException e) {
s_logger.debug("Failed to change vm state: " + e.toString());
throw new CloudRuntimeException("Failed to change vm state: " + e.toString());
}
}
}
return vm;
}
@Override @Override
public <T extends VMInstanceVO> T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, public <T extends VMInstanceVO> T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException,
VirtualMachineMigrationException { VirtualMachineMigrationException {

View File

@ -28,6 +28,7 @@ import org.apache.log4j.Logger;
import com.cloud.host.HostVO; import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDaoImpl; import com.cloud.host.dao.HostDaoImpl;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.db.Attribute; import com.cloud.utils.db.Attribute;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
@ -279,7 +280,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
} }
@Override @Override
public boolean updateState(State oldState, Event event, State newState, VirtualMachine vm, Long hostId) { public boolean updateState(State oldState, Event event, State newState, VirtualMachine vm, Object opaque) {
if (newState == null) { if (newState == null) {
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("There's no way to transition from old state: " + oldState.toString() + " event: " + event.toString()); s_logger.debug("There's no way to transition from old state: " + oldState.toString() + " event: " + event.toString());
@ -287,6 +288,10 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
return false; return false;
} }
@SuppressWarnings("unchecked")
Pair<Long, Long> hosts = (Pair<Long,Long>)opaque;
Long newHostId = hosts.second();
VMInstanceVO vmi = (VMInstanceVO)vm; VMInstanceVO vmi = (VMInstanceVO)vm;
Long oldHostId = vmi.getHostId(); Long oldHostId = vmi.getHostId();
Long oldUpdated = vmi.getUpdated(); Long oldUpdated = vmi.getUpdated();
@ -302,7 +307,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
UpdateBuilder ub = getUpdateBuilder(vmi); UpdateBuilder ub = getUpdateBuilder(vmi);
ub.set(vmi, "state", newState); ub.set(vmi, "state", newState);
ub.set(vmi, "hostId", hostId); ub.set(vmi, "hostId", newHostId);
ub.set(vmi, "podIdToDeployIn", vmi.getPodIdToDeployIn()); ub.set(vmi, "podIdToDeployIn", vmi.getPodIdToDeployIn());
ub.set(vmi, _updateTimeAttr, new Date()); ub.set(vmi, _updateTimeAttr, new Date());

View File

@ -382,6 +382,7 @@ CREATE TABLE `cloud`.`volumes` (
`account_id` bigint unsigned NOT NULL COMMENT 'owner. foreign key to account table', `account_id` bigint unsigned NOT NULL COMMENT 'owner. foreign key to account table',
`domain_id` bigint unsigned NOT NULL COMMENT 'the domain that the owner belongs to', `domain_id` bigint unsigned NOT NULL COMMENT 'the domain that the owner belongs to',
`pool_id` bigint unsigned COMMENT 'pool it belongs to. foreign key to storage_pool table', `pool_id` bigint unsigned COMMENT 'pool it belongs to. foreign key to storage_pool table',
`last_pool_id` bigint unsigned COMMENT 'last pool it belongs to.',
`instance_id` bigint unsigned NULL COMMENT 'vm instance it belongs to. foreign key to vm_instance table', `instance_id` bigint unsigned NULL COMMENT 'vm instance it belongs to. foreign key to vm_instance table',
`device_id` bigint unsigned NULL COMMENT 'which device inside vm instance it is ', `device_id` bigint unsigned NULL COMMENT 'which device inside vm instance it is ',
`name` varchar(255) COMMENT 'A user specified name for the volume', `name` varchar(255) COMMENT 'A user specified name for the volume',
@ -404,6 +405,7 @@ CREATE TABLE `cloud`.`volumes` (
`removed` datetime COMMENT 'Date removed. not null if removed', `removed` datetime COMMENT 'Date removed. not null if removed',
`state` varchar(32) COMMENT 'State machine', `state` varchar(32) COMMENT 'State machine',
`chain_info` text COMMENT 'save possible disk chain info in primary storage', `chain_info` text COMMENT 'save possible disk chain info in primary storage',
`update_count` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'date state was updated',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
INDEX `i_volumes__removed`(`removed`), INDEX `i_volumes__removed`(`removed`),
INDEX `i_volumes__pod_id`(`pod_id`), INDEX `i_volumes__pod_id`(`pod_id`),
@ -412,9 +414,11 @@ CREATE TABLE `cloud`.`volumes` (
INDEX `i_volumes__account_id`(`account_id`), INDEX `i_volumes__account_id`(`account_id`),
CONSTRAINT `fk_volumes__pool_id` FOREIGN KEY `fk_volumes__pool_id` (`pool_id`) REFERENCES `storage_pool` (`id`), CONSTRAINT `fk_volumes__pool_id` FOREIGN KEY `fk_volumes__pool_id` (`pool_id`) REFERENCES `storage_pool` (`id`),
INDEX `i_volumes__pool_id`(`pool_id`), INDEX `i_volumes__pool_id`(`pool_id`),
INDEX `i_volumes__last_pool_id`(`last_pool_id`),
CONSTRAINT `fk_volumes__instance_id` FOREIGN KEY `fk_volumes__instance_id` (`instance_id`) REFERENCES `vm_instance` (`id`) ON DELETE CASCADE, CONSTRAINT `fk_volumes__instance_id` FOREIGN KEY `fk_volumes__instance_id` (`instance_id`) REFERENCES `vm_instance` (`id`) ON DELETE CASCADE,
INDEX `i_volumes__instance_id`(`instance_id`), INDEX `i_volumes__instance_id`(`instance_id`),
INDEX `i_volumes__state`(`state`) INDEX `i_volumes__state`(`state`),
INDEX `i_volumes__update_count`(`update_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `cloud`.`snapshots` ( CREATE TABLE `cloud`.`snapshots` (

View File

@ -20,12 +20,14 @@ class jobStatus(object):
self.duration = None self.duration = None
self.jobId = None self.jobId = None
self.responsecls = None self.responsecls = None
def __str__(self):
return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
class workThread(threading.Thread): class workThread(threading.Thread):
def __init__(self, in_queue, outqueue, apiClient, db=None, lock=None): def __init__(self, in_queue, outqueue, apiClient, db=None, lock=None):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.inqueue = in_queue self.inqueue = in_queue
self.output = outqueue self.output = outqueue
self.connection = apiClient.connection self.connection = apiClient.connection.__copy__()
self.db = None self.db = None
self.lock = lock self.lock = lock

View File

@ -51,6 +51,7 @@ class cloudConnection(object):
requestUrl += "&signature=%s"%sig requestUrl += "&signature=%s"%sig
self.connection.request("GET", "/client/api?%s"%requestUrl) self.connection.request("GET", "/client/api?%s"%requestUrl)
return self.connection.getresponse().read() return self.connection.getresponse().read()
def make_request_without_auth(self, command, requests={}): def make_request_without_auth(self, command, requests={}):

View File

@ -20,6 +20,6 @@ package com.cloud.utils.fsm;
public interface StateDao <S,E,V> { public interface StateDao <S,E,V> {
boolean updateState(S currentState, E event, S nextState, V vo, Long id); boolean updateState(S currentState, E event, S nextState, V vo, Object data);
} }

View File

@ -27,11 +27,11 @@ public interface StateListener <S,E,V> {
* @param newState VM's new state * @param newState VM's new state
* @param vo the VM instance * @param vo the VM instance
* @param status the state transition is allowed or not * @param status the state transition is allowed or not
* @param id host id * @param opaque host id
* @param vmDao VM dao * @param vmDao VM dao
* @return * @return
*/ */
public boolean preStateTransitionEvent(S oldState, E event, S newState, V vo, boolean status, Long id); public boolean preStateTransitionEvent(S oldState, E event, S newState, V vo, boolean status, Object opaque);
/** /**
* Event is triggered after state machine transition finished * Event is triggered after state machine transition finished
@ -42,5 +42,5 @@ public interface StateListener <S,E,V> {
* @param status the state transition is allowed or not * @param status the state transition is allowed or not
* @return * @return
*/ */
public boolean postStateTransitionEvent(S oldState, E event, S newState, V vo, boolean status, Long id); public boolean postStateTransitionEvent(S oldState, E event, S newState, V vo, boolean status, Object opaque);
} }

View File

@ -99,7 +99,7 @@ public class StateMachine2<S, E, V extends StateObject<S>> {
} }
public boolean transitTo(V vo, E e, Long id, StateDao<S,E,V> dao) throws NoTransitionException { public boolean transitTo(V vo, E e, Object opaque, StateDao<S,E,V> dao) throws NoTransitionException {
S currentState = vo.getState(); S currentState = vo.getState();
S nextState = getNextState(currentState, e); S nextState = getNextState(currentState, e);
@ -109,17 +109,16 @@ public class StateMachine2<S, E, V extends StateObject<S>> {
} }
for (StateListener<S,E, V> listener : _listeners) { for (StateListener<S,E, V> listener : _listeners) {
listener.preStateTransitionEvent(currentState, e, nextState, vo, transitionStatus, id); listener.preStateTransitionEvent(currentState, e, nextState, vo, transitionStatus, opaque);
} }
Long oldHostId = vo.getHostId(); transitionStatus = dao.updateState(currentState, e, nextState, vo, opaque);
transitionStatus = dao.updateState(currentState, e, nextState, vo, id);
if (!transitionStatus) { if (!transitionStatus) {
return false; return false;
} }
for (StateListener<S,E, V> listener : _listeners) { for (StateListener<S,E, V> listener : _listeners) {
listener.postStateTransitionEvent(currentState, e, nextState, vo, transitionStatus, oldHostId); listener.postStateTransitionEvent(currentState, e, nextState, vo, transitionStatus, opaque);
} }
return true; return true;

View File

@ -23,5 +23,4 @@ public interface StateObject<S> {
* @return finite state. * @return finite state.
*/ */
S getState(); S getState();
Long getHostId();
} }