more changes

This commit is contained in:
Alex Huang 2010-09-27 12:17:18 -07:00
parent 49143dbe4a
commit c443867f28
28 changed files with 512 additions and 178 deletions

View File

@ -137,7 +137,7 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateCommand;
import com.cloud.agent.api.storage.DestroyCommand;
import com.cloud.agent.api.storage.DownloadAnswer;
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
import com.cloud.agent.api.to.StoragePoolTO;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.agent.resource.computing.LibvirtStoragePoolDef.poolType;
import com.cloud.agent.resource.computing.LibvirtStorageVolumeDef.volFormat;
@ -1173,7 +1173,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
protected Answer execute(CreateCommand cmd) {
StoragePoolTO pool = cmd.getPool();
StorageFilerTO pool = cmd.getPool();
DiskProfile dskch = cmd.getDiskCharacteristics();
StorageVol tmplVol = null;
StoragePool primaryPool = null;

View File

@ -18,19 +18,16 @@
package com.cloud.agent.api.storage;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.to.StoragePoolTO;
import com.cloud.storage.StoragePoolVO;
import com.cloud.storage.VolumeVO;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.storage.StoragePool;
import com.cloud.vm.DiskProfile;
import com.cloud.vm.VMInstanceVO;
public class CreateCommand extends Command {
private long volId;
private StoragePoolTO pool;
private StorageFilerTO pool;
private DiskProfile diskCharacteristics;
private String templateUrl;
private long size;
private String instanceName;
protected CreateCommand() {
super();
@ -45,8 +42,8 @@ public class CreateCommand extends Command {
* @param templateUrl
* @param pool
*/
public CreateCommand(VolumeVO vol, VMInstanceVO vm, DiskProfile diskCharacteristics, String templateUrl, StoragePoolVO pool) {
this(vol, vm, diskCharacteristics, pool, 0);
public CreateCommand(DiskProfile diskCharacteristics, String templateUrl, StorageFilerTO pool) {
this(diskCharacteristics, pool, 0);
this.templateUrl = templateUrl;
}
@ -58,13 +55,20 @@ public class CreateCommand extends Command {
* @param diskCharacteristics
* @param pool
*/
public CreateCommand(VolumeVO vol, VMInstanceVO vm, DiskProfile diskCharacteristics, StoragePoolVO pool, long size) {
this.volId = vol.getId();
public CreateCommand(DiskProfile diskCharacteristics, StorageFilerTO pool, long size) {
this.volId = diskCharacteristics.getVolumeId();
this.diskCharacteristics = diskCharacteristics;
this.pool = new StoragePoolTO(pool);
this.pool = pool;
this.templateUrl = null;
this.size = size;
//this.instanceName = vm.getInstanceName();
}
public CreateCommand(DiskProfile diskCharacteristics, String templateUrl, StoragePool pool) {
this(diskCharacteristics, templateUrl, new StorageFilerTO(pool));
}
public CreateCommand(DiskProfile diskCharacteristics, StoragePool pool, long size) {
this(diskCharacteristics, new StorageFilerTO(pool), size);
}
@Override
@ -76,7 +80,7 @@ public class CreateCommand extends Command {
return templateUrl;
}
public StoragePoolTO getPool() {
public StorageFilerTO getPool() {
return pool;
}
@ -92,7 +96,8 @@ public class CreateCommand extends Command {
return this.size;
}
@Deprecated
public String getInstanceName() {
return instanceName;
return null;
}
}

View File

@ -17,11 +17,10 @@
*/
package com.cloud.agent.api.to;
import com.cloud.storage.StoragePoolVO;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StoragePool;
public class StoragePoolTO {
public class StorageFilerTO {
long id;
String uuid;
String host;
@ -29,7 +28,7 @@ public class StoragePoolTO {
int port;
StoragePoolType type;
public StoragePoolTO(StoragePoolVO pool) {
public StorageFilerTO(StoragePool pool) {
this.id = pool.getId();
this.host = pool.getHostAddress();
this.port = pool.getPort();
@ -62,7 +61,7 @@ public class StoragePoolTO {
return type;
}
protected StoragePoolTO() {
protected StorageFilerTO() {
}
@Override

View File

@ -20,18 +20,39 @@ package com.cloud.exception;
import com.cloud.utils.SerialVersionUID;
/**
* This exception is thrown when the storage device can not be reached.
*
* This exception is thrown when storage for a VM is unavailable.
* If the cause is due to storage pool unavailable, calling
* getOffendingObject() will return the object that we have
* problem with.
*
*/
public class StorageUnavailableException extends AgentUnavailableException {
public class StorageUnavailableException extends Exception {
Object _obj;
private static final long serialVersionUID = SerialVersionUID.StorageUnavailableException;
public StorageUnavailableException(long hostId) {
super(hostId);
}
public StorageUnavailableException(String msg) {
super(msg, -1);
super(msg);
}
public StorageUnavailableException(String msg, Throwable cause) {
super(msg, cause);
}
public StorageUnavailableException(String msg, Object cause) {
super(msg);
_obj = cause;
}
public StorageUnavailableException(String msg, Object obj, Throwable cause) {
super(msg, cause);
_obj = obj;
}
/**
* @return object that caused this problem. It can either be a StoragePool or volume.
*/
public Object getOffendingObject() {
return _obj;
}
}

View File

@ -23,10 +23,10 @@ public interface NetworkConfiguration {
}
enum State implements FiniteState<State, Event> {
Allocated, // Indicates the network configuration is in allocated but not setup.
Setup, // Indicates the network configuration is setup.
Implemented, // Indicates the network configuration is in use.
Destroying;
Allocated("Indicates the network configuration is in allocated but not setup"),
Setup("Indicates the network configuration is setup"),
Implemented("Indicates the network configuration is in use"),
Destroying("Indicates the network configuration is being destroyed");
@Override
public StateMachine<State, Event> getStateMachine() {
@ -48,7 +48,23 @@ public interface NetworkConfiguration {
return s_fsm.getPossibleEvents(this);
}
String _description;
@Override
public String getDescription() {
return _description;
}
private State(String description) {
_description = description;
}
private static StateMachine<State, Event> s_fsm = new StateMachine<State, Event>();
static {
s_fsm.addTransition(State.Allocated, Event.ImplementNetwork, State.Implemented);
s_fsm.addTransition(State.Implemented, Event.DestroyNetwork, State.Destroying);
}
}

View File

@ -26,12 +26,14 @@ public interface Resource {
}
enum State implements FiniteState<State, Event> {
Allocated, // Resource is allocated
Reserving, // Resource is being reserved right now.
Reserved, // Resource is reserved
Releasing, // Resource is being released.
Ready; // Resource is ready which means it does not need to go through reservation.
Allocated("Resource is allocated but not reserved"),
Reserving("Resource is being reserved right now"),
Reserved("Resource has been reserved."),
Releasing("Resource is being released"),
Ready("Resource is ready which means it doesn't need to go through resservation");
String _description;
@Override
public StateMachine<State, Event> getStateMachine() {
@ -54,12 +56,24 @@ public interface Resource {
return s_fsm.getPossibleEvents(this);
}
private State(String description) {
_description = description;
}
@Override
public String getDescription() {
return _description;
}
final static private StateMachine<State, Event> s_fsm = new StateMachine<State, Event>();
static {
s_fsm.addTransition(State.Allocated, Event.ReservationRequested, State.Reserving);
s_fsm.addTransition(State.Reserving, Event.CancelRequested, State.Allocated);
s_fsm.addTransition(State.Reserving, Event.OperationCompleted, State.Reserved);
s_fsm.addTransition(State.Reserving, Event.OperationFailed, State.Allocated);
s_fsm.addTransition(State.Reserved, Event.ReleaseRequested, State.Releasing);
s_fsm.addTransition(State.Releasing, Event.OperationCompleted, State.Allocated);
s_fsm.addTransition(State.Releasing, Event.OperationFailed, State.Reserved);
}
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
* It is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.cloud.storage;
import com.cloud.utils.component.Adapter;
/**
* StorageGuru understands about how to implement different
* types of storage pools.
*/
public interface StorageGuru extends Adapter {
void createVolume();
void prepareVolume();
void destroyVolume();
}

View File

@ -101,4 +101,8 @@ public interface StoragePool {
* @return the storage pool status
*/
Status getStatus();
int getPort();
Long getPodId();
}

View File

@ -18,10 +18,14 @@
package com.cloud.storage;
import java.util.Date;
import java.util.List;
import java.util.Set;
import com.cloud.domain.PartOf;
import com.cloud.template.BasedOn;
import com.cloud.user.OwnedBy;
import com.cloud.utils.fsm.FiniteState;
import com.cloud.utils.fsm.StateMachine;
public interface Volume extends PartOf, OwnedBy, BasedOn {
@ -29,14 +33,70 @@ public interface Volume extends PartOf, OwnedBy, BasedOn {
enum MirrorState {NOT_MIRRORED, ACTIVE, DEFUNCT};
enum State {
Allocated,
Creating,
Created,
Corrupted,
ToBeDestroyed,
Expunging,
Destroyed
enum State implements FiniteState<State, Event> {
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."),
Ready("The volume is ready to be used."),
Destroy("The volume is set to be desctroyed but can be recovered."),
Expunging("The volume is being destroyed. There's no way to recover."),
Destroyed("The volume is destroyed. Should be removed.");
String _description;
private State(String description) {
_description = description;
}
@Override
public StateMachine<State, Event> getStateMachine() {
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() {
return _description;
}
private final static StateMachine<State, Event> s_fsm = new StateMachine<State, Event>();
static {
s_fsm.addTransition(Allocated, Event.Create, Creating);
s_fsm.addTransition(Allocated, Event.Destroy, Destroyed);
s_fsm.addTransition(Creating, Event.OperationRetry, Creating);
s_fsm.addTransition(Creating, Event.OperationFailed, Allocated);
s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Creating, Event.Destroy, Expunging);
s_fsm.addTransition(Ready, Event.Destroy, Destroy);
s_fsm.addTransition(Destroy, Event.Expunge, Expunging);
s_fsm.addTransition(Destroy, Event.Recover, Ready);
s_fsm.addTransition(Expunging, Event.OperationSucceeded, Destroyed);
s_fsm.addTransition(Expunging, Event.OperationFailed, Destroy);
s_fsm.addTransition(Allocated, Event.Destroy, Destroyed);
}
}
enum Event {
Create,
OperationFailed,
OperationSucceeded,
OperationRetry,
Destroy,
Recover,
Expunge;
}
enum SourceType {

View File

@ -26,21 +26,28 @@ import com.cloud.utils.fsm.StateMachine;
import com.cloud.vm.VirtualMachine.Event;
public enum State implements FiniteState<State, Event> {
Creating(true),
Starting(true),
Running(false),
Stopping(true),
Stopped(false),
Destroyed(false),
Expunging(true),
Migrating(true),
Error(false),
Unknown(false);
Creating(true, "VM is being created"),
Starting(true, "VM is being started. At this state, you should find host id filled which means it's being started on that host."),
Running(false, "VM is running. host id has the host that it is running on."),
Stopping(true, "VM is being stopped. host id has the host that it is being stopped on."),
Stopped(false, "VM is stopped. host id should be null."),
Destroyed(false, "VM is marked for destroy."),
Expunging(true, "VM is being expunged."),
Migrating(true, "VM is being migrated. host id holds to from host"),
Error(false, "VM is in error"),
Unknown(false, "VM state is unknown.");
private final boolean _transitional;
String _description;
private State(boolean transitional) {
private State(boolean transitional, String description) {
_transitional = transitional;
_description = description;
}
@Override
public String getDescription() {
return _description;
}
public boolean isTransitional() {
@ -67,6 +74,7 @@ public enum State implements FiniteState<State, Event> {
return s_fsm;
}
protected static final StateMachine<State, VirtualMachine.Event> s_fsm = new StateMachine<State, VirtualMachine.Event>();
static {
s_fsm.addTransition(null, VirtualMachine.Event.CreateRequested, State.Creating);

View File

@ -145,7 +145,7 @@ import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
import com.cloud.agent.api.storage.ShareAnswer;
import com.cloud.agent.api.storage.ShareCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.StoragePoolTO;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.exception.InternalErrorException;
@ -4449,7 +4449,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
protected Answer execute(ModifyStoragePoolCommand cmd) {
StoragePoolVO pool = cmd.getPool();
StoragePoolTO poolTO = new StoragePoolTO(pool);
StorageFilerTO poolTO = new StorageFilerTO(pool);
try {
Connection conn = getConnection();
@ -4478,7 +4478,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
protected Answer execute(DeleteStoragePoolCommand cmd) {
StoragePoolVO pool = cmd.getPool();
StoragePoolTO poolTO = new StoragePoolTO(pool);
StorageFilerTO poolTO = new StorageFilerTO(pool);
try {
Connection conn = getConnection();
SR sr = getStorageRepository(conn, poolTO);
@ -4719,7 +4719,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
@Override
public CreateAnswer execute(CreateCommand cmd) {
StoragePoolTO pool = cmd.getPool();
StorageFilerTO pool = cmd.getPool();
DiskProfile dskch = cmd.getDiskCharacteristics();
VDI vdi = null;
@ -4893,7 +4893,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
}
}
protected SR getIscsiSR(StoragePoolTO pool) {
protected SR getIscsiSR(StorageFilerTO pool) {
Connection conn = getConnection();
synchronized (pool.getUuid().intern()) {
Map<String, String> deviceConfig = new HashMap<String, String>();
@ -5003,7 +5003,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
}
}
protected SR getNfsSR(StoragePoolTO pool) {
protected SR getNfsSR(StorageFilerTO pool) {
Connection conn = getConnection();
Map<String, String> deviceConfig = new HashMap<String, String>();
@ -5124,7 +5124,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
public CopyVolumeAnswer execute(final CopyVolumeCommand cmd) {
String volumeUUID = cmd.getVolumePath();
StoragePoolVO pool = cmd.getPool();
StoragePoolTO poolTO = new StoragePoolTO(pool);
StorageFilerTO poolTO = new StorageFilerTO(pool);
String secondaryStorageURL = cmd.getSecondaryStorageURL();
URI uri = null;
@ -6041,7 +6041,7 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
}
}
protected SR getStorageRepository(Connection conn, StoragePoolTO pool) {
protected SR getStorageRepository(Connection conn, StorageFilerTO pool) {
Set<SR> srs;
try {
srs = SR.getByNameLabel(conn, pool.getUuid());

View File

@ -28,6 +28,8 @@ import com.cloud.agent.api.to.VolumeTO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientStorageCapacityException;
import com.cloud.exception.InternalErrorException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceInUseException;
@ -229,9 +231,9 @@ public interface StorageManager extends Manager {
*/
boolean volumeOnSharedStoragePool(VolumeVO volume);
Answer[] sendToPool(StoragePoolVO pool, Command[] cmds, boolean stopOnError);
Answer[] sendToPool(StoragePool pool, Command[] cmds, boolean stopOnError);
Answer sendToPool(StoragePoolVO pool, Command cmd);
Answer sendToPool(StoragePool pool, Command cmd);
/**
* Checks that one of the following is true:
@ -314,9 +316,9 @@ public interface StorageManager extends Manager {
<T extends VMInstanceVO> DiskProfile allocateTemplatedVolume(VolumeType type, String name, DiskOfferingVO offering, VMTemplateVO template, T vm, AccountVO owner);
<T extends VMInstanceVO> void create(T vm);
Long findHostIdForStoragePool(StoragePoolVO pool);
Long findHostIdForStoragePool(StoragePool pool);
void createCapacityEntry(StoragePoolVO storagePool, long allocated);
VolumeTO[] prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException;
VolumeTO[] prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, ConcurrentOperationException;
}

View File

@ -82,7 +82,8 @@ public class StoragePoolVO implements StoragePool {
@Enumerated(value=EnumType.STRING)
private Status status;
public long getId() {
@Override
public long getId() {
return id;
}
@ -95,19 +96,23 @@ public class StoragePoolVO implements StoragePool {
// TODO Auto-generated constructor stub
}
public String getName() {
@Override
public String getName() {
return name;
}
public String getUuid() {
@Override
public String getUuid() {
return uuid;
}
public StoragePoolType getPoolType() {
@Override
public StoragePoolType getPoolType() {
return poolType;
}
public Date getCreated() {
@Override
public Date getCreated() {
return created;
}
@ -115,19 +120,23 @@ public class StoragePoolVO implements StoragePool {
return removed;
}
public Date getUpdateTime() {
@Override
public Date getUpdateTime() {
return updateTime;
}
public long getDataCenterId() {
@Override
public long getDataCenterId() {
return dataCenterId;
}
public long getAvailableBytes() {
@Override
public long getAvailableBytes() {
return availableBytes;
}
public long getCapacityBytes() {
@Override
public long getCapacityBytes() {
return capacityBytes;
}
@ -160,10 +169,12 @@ public class StoragePoolVO implements StoragePool {
this.clusterId = clusterId;
}
@Override
public String getHostAddress() {
return hostAddress;
}
@Override
public String getPath() {
return path;
}
@ -217,10 +228,12 @@ public class StoragePoolVO implements StoragePool {
return port;
}
@Override
public boolean isShared() {
return poolType.isShared();
}
@Override
public boolean isLocal() {
return !poolType.isShared();
}
@ -252,6 +265,10 @@ public class StoragePoolVO implements StoragePool {
this.name = name;
}
public boolean isInMaintenance() {
return status == Status.PrepareForMaintenance || status == Status.Maintenance || status == Status.ErrorInMaintenance || removed != null;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StoragePoolVO) || obj == null) {

View File

@ -236,6 +236,10 @@ public class VolumeVO implements Volume {
this.status = AsyncInstanceCreateStatus.Created;
this.recreatable = false;
}
public void setState(State state) {
this.state = state;
}
public boolean isRecreatable() {
return recreatable;
@ -245,7 +249,8 @@ public class VolumeVO implements Volume {
return iscsiName;
}
public long getId() {
@Override
public long getId() {
return id;
}

View File

@ -19,6 +19,7 @@ package com.cloud.storage.dao;
import java.util.List;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.utils.Pair;
@ -47,4 +48,13 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long> {
List<VolumeVO> findCreatedByInstance(long id);
List<VolumeVO> findByPoolId(long poolId);
List<VolumeVO> findByInstanceAndDeviceId(long instanceId, long deviceId);
List<VolumeVO> findUsableVolumesForInstance(long instanceId);
/**
* 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;
}

View File

@ -29,11 +29,13 @@ import javax.ejb.Local;
import org.apache.log4j.Logger;
import com.cloud.async.AsyncInstanceCreateStatus;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.storage.Volume;
import com.cloud.storage.Volume.MirrorState;
import com.cloud.storage.Volume.VolumeType;
import com.cloud.storage.VolumeVO;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Attribute;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.GenericSearchBuilder;
@ -42,6 +44,7 @@ import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.SearchCriteria.Func;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.UpdateBuilder;
import com.cloud.utils.exception.CloudRuntimeException;
@Local(value=VolumeDao.class) @DB(txn=false)
@ -62,6 +65,10 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
protected final SearchBuilder<VolumeVO> RemovedButNotDestroyedSearch;
protected final SearchBuilder<VolumeVO> PoolIdSearch;
protected final SearchBuilder<VolumeVO> InstanceAndDeviceIdSearch;
protected final SearchBuilder<VolumeVO> InstanceStatesSearch;
protected final SearchBuilder<VolumeVO> IdStateSearch;
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_ID_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ?";
@ -143,6 +150,15 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
return listBy(sc);
}
@Override
public List<VolumeVO> findUsableVolumesForInstance(long instanceId) {
SearchCriteria<VolumeVO> sc = InstanceStatesSearch.create();
sc.setParameters("instance", instanceId);
sc.setParameters("states", Volume.State.Creating, Volume.State.Ready, Volume.State.Allocated);
return listBy(sc);
}
@Override
public List<VolumeVO> findByInstanceAndType(long id, VolumeType vType) {
SearchCriteria<VolumeVO> sc = InstanceAndTypeSearch.create();
@ -271,6 +287,28 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
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 = IdStateSearch.create();
sc.setParameters("id", vol.getId());
sc.setParameters("state", vol.getState());
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;
}
protected VolumeDaoImpl() {
AccountIdSearch = createSearchBuilder();
AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
@ -353,6 +391,19 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
RemovedButNotDestroyedSearch.and("destroyed", RemovedButNotDestroyedSearch.entity().getDestroyed(), SearchCriteria.Op.EQ);
RemovedButNotDestroyedSearch.and("removed", RemovedButNotDestroyedSearch.entity().getRemoved(), SearchCriteria.Op.NNULL);
RemovedButNotDestroyedSearch.done();
InstanceStatesSearch = createSearchBuilder();
InstanceStatesSearch.and("instance", InstanceStatesSearch.entity().getInstanceId(), SearchCriteria.Op.EQ);
InstanceStatesSearch.and("states", InstanceStatesSearch.entity().getState(), SearchCriteria.Op.IN);
InstanceStatesSearch.done();
IdStateSearch = createSearchBuilder();
IdStateSearch.and("id", IdStateSearch.entity().getId(), SearchCriteria.Op.EQ);
IdStateSearch.and("state", IdStateSearch.entity().getState(), SearchCriteria.Op.EQ);
IdStateSearch.done();
_stateAttr = _allAttributes.get("state");
assert _stateAttr != null : "Couldn't get the state attribute";
}
@Override @DB(txn=false)

View File

@ -516,7 +516,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach
@Override
public ConsoleProxyVO startProxy(long proxyVmId, long startEventId) {
try {
return start(proxyVmId, startEventId);
return start2(proxyVmId, startEventId);
} catch (StorageUnavailableException e) {
s_logger.warn("Exception while trying to start console proxy", e);
return null;
@ -852,7 +852,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach
if (s_logger.isDebugEnabled())
s_logger.debug("Assign console proxy from a newly started instance for request from data center : " + dataCenterId);
Map<String, Object> context = createProxyInstance(dataCenterId);
Map<String, Object> context = createProxyInstance2(dataCenterId);
long proxyVmId = (Long) context.get("proxyVmId");
if (proxyVmId == 0) {
@ -892,7 +892,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach
if (s_logger.isDebugEnabled())
s_logger.debug("Assign console proxy from a newly started instance for request from data center : " + dataCenterId);
Map<String, Object> context = createProxyInstance(dataCenterId);
Map<String, Object> context = createProxyInstance2(dataCenterId);
long proxyVmId = (Long) context.get("proxyVmId");
if (proxyVmId == 0) {
@ -1417,7 +1417,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach
try {
if (proxyLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_SYNC)) {
try {
readyProxy = start(readyProxy.getId(), 0);
readyProxy = start2(readyProxy.getId(), 0);
} finally {
proxyLock.unlock();
}
@ -2150,7 +2150,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach
if (!_storageMgr.share(proxy, vols, routingHost, false)) {
s_logger.warn("Can not share " + proxy.getName());
throw new StorageUnavailableException(vol.getPoolId());
throw new StorageUnavailableException("Can not share " + proxy.getName(), vol);
}
Answer answer = _agentMgr.easySend(routingHost.getId(), cmd);

View File

@ -2139,7 +2139,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager
if( ! _storageMgr.share(router, vols, routingHost, false) ) {
s_logger.warn("Can not share " + vol.getPath() + " to " + router.getName() );
throw new StorageUnavailableException(vol.getPoolId());
throw new StorageUnavailableException("Can not share " + vol.getPath() + " to " + router.getName(), vol);
}
final Answer answer = _agentMgr.easySend(routingHost.getId(), cmd);

View File

@ -26,7 +26,6 @@ import java.util.Enumeration;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -56,6 +55,7 @@ import com.cloud.agent.api.storage.CreateAnswer;
import com.cloud.agent.api.storage.CreateCommand;
import com.cloud.agent.api.storage.DeleteTemplateCommand;
import com.cloud.agent.api.storage.DestroyCommand;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.alert.AlertManager;
import com.cloud.api.BaseCmd;
@ -84,6 +84,7 @@ import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.DiscoveryException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientStorageCapacityException;
import com.cloud.exception.InternalErrorException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
@ -103,6 +104,7 @@ import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.Volume.Event;
import com.cloud.storage.Volume.MirrorState;
import com.cloud.storage.Volume.SourceType;
import com.cloud.storage.Volume.VolumeType;
@ -173,10 +175,6 @@ public class StorageManagerImpl implements StorageManager {
@Inject protected ConsoleProxyDao _consoleProxyDao;
@Inject protected DetailsDao _detailsDao;
@Inject protected SnapshotDao _snapshotDao;
@Inject(adapter=StoragePoolAllocator.class)
protected Adapters<StoragePoolAllocator> _storagePoolAllocators;
@Inject(adapter=StoragePoolDiscoverer.class)
protected Adapters<StoragePoolDiscoverer> _discoverers;
@Inject protected StoragePoolHostDao _storagePoolHostDao;
@Inject protected AlertManager _alertMgr;
@Inject protected VMTemplateHostDao _vmTemplateHostDao = null;
@ -197,6 +195,11 @@ public class StorageManagerImpl implements StorageManager {
@Inject protected ServiceOfferingDao _offeringDao;
@Inject protected UserDao _userDao;
@Inject(adapter=StoragePoolAllocator.class)
protected Adapters<StoragePoolAllocator> _storagePoolAllocators;
@Inject(adapter=StoragePoolDiscoverer.class)
protected Adapters<StoragePoolDiscoverer> _discoverers;
protected SearchBuilder<VMTemplateHostVO> HostTemplateStatesSearch;
protected SearchBuilder<StoragePoolVO> PoolsUsedByVmSearch;
@ -272,79 +275,6 @@ public class StorageManagerImpl implements StorageManager {
return vols.get(0);
}
@Override
public VolumeTO[] prepare(VirtualMachineProfile vm, DeployDestination dest) {
List<VolumeVO> vols = _volsDao.findCreatedByInstance(vm.getId());
List<VolumeVO> recreateVols = new ArrayList<VolumeVO>(vols.size());
Host host = dest.getHost();
VolumeTO[] disks = new VolumeTO[vols.size()];
int i = 0;
Iterator<VolumeVO> it = vols.iterator();
while (it.hasNext()) {
VolumeVO vol = it.next();
if (vol.isRecreatable()) {
it.remove();
//if we have a system vm
//get the storage pool
//if pool is in maintenance
//add to recreate vols, and continue
if(vm.getType().equals(VirtualMachine.Type.ConsoleProxy) || vm.getType().equals(VirtualMachine.Type.DomainRouter) || vm.getType().equals(VirtualMachine.Type.SecondaryStorageVm))
{
StoragePoolVO sp = _storagePoolDao.findById(vol.getPoolId());
if(sp!=null && sp.getStatus().equals(Status.PrepareForMaintenance))
{
recreateVols.add(vol);
continue;
}
}
StoragePoolHostVO ph = _storagePoolHostDao.findByPoolHost(vol.getPoolId(), host.getId());
if (ph == null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Must recreate " + vol + " since " + vol.getPoolId() + " has is not hooked up with host " + host.getId());
}
recreateVols.add(vol);
}
}
}
for (VolumeVO vol : recreateVols) {
VolumeVO create = allocateDuplicateVolume(vol);
vols.add(vol);
}
/*
create.setDiskOfferingId(vol.getDiskOfferingId());
create.setDeviceId(vol.getDeviceId());
create = _volsDao.persist(create);
VMTemplateVO template = _templateDao.findById(create.getTemplateId());
DataCenterVO dc = _dcDao.findById(create.getDataCenterId());
HostPodVO pod = _podDao.findById(host.getPodId());
DiskOfferingVO diskOffering = null;
diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId());
ServiceOfferingVO offering;
if (vm instanceof UserVmVO) {
offering = _offeringDao.findById(((UserVmVO)vm).getServiceOfferingId());
} else {
offering = _offeringDao.findById(vol.getDiskOfferingId());
}
VolumeVO created = createVolume(create, vm, template, dc, pod, host.getClusterId(), offering, diskOffering, new ArrayList<StoragePoolVO>(),0);
if (created == null) {
break;
}
createds.add(created);
for (VolumeVO vol : recreateVols) {
_volsDao.remove(vol.getId());
}
disks[i++] = null;
}
*/
return disks;
}
VolumeVO allocateDuplicateVolume(VolumeVO oldVol) {
VolumeVO newVol = new VolumeVO(oldVol.getVolumeType(), oldVol.getName(), oldVol.getDataCenterId(), oldVol.getDomainId(), oldVol.getAccountId(), oldVol.getDiskOfferingId(), oldVol.getSize());
newVol.setTemplateId(oldVol.getTemplateId());
@ -487,7 +417,7 @@ public class StorageManagerImpl implements StorageManager {
}
@Override
public Long findHostIdForStoragePool(StoragePoolVO pool) {
public Long findHostIdForStoragePool(StoragePool pool) {
List<StoragePoolHostVO> poolHosts = _poolHostDao.listByHostStatus(pool.getId(), Status.Up);
if (poolHosts.size() == 0) {
@ -498,7 +428,7 @@ public class StorageManagerImpl implements StorageManager {
}
@Override
public Answer[] sendToPool(StoragePoolVO pool, Command[] cmds, boolean stopOnError) {
public Answer[] sendToPool(StoragePool pool, Command[] cmds, boolean stopOnError) {
List<StoragePoolHostVO> poolHosts = _poolHostDao.listByHostStatus(pool.getId(), Status.Up);
Collections.shuffle(poolHosts);
@ -520,7 +450,7 @@ public class StorageManagerImpl implements StorageManager {
}
@Override
public Answer sendToPool(StoragePoolVO pool, Command cmd) {
public Answer sendToPool(StoragePool pool, Command cmd) {
Command[] cmds = new Command[]{cmd};
Answer[] answers = sendToPool(pool, cmds, true);
if (answers == null) {
@ -885,9 +815,9 @@ public class StorageManagerImpl implements StorageManager {
if (tmpltStoredOn == null) {
continue;
}
cmd = new CreateCommand(volume, vm, dskCh, tmpltStoredOn.getLocalDownloadPath(), pool);
cmd = new CreateCommand(dskCh, tmpltStoredOn.getLocalDownloadPath(), new StorageFilerTO(pool));
} else {
cmd = new CreateCommand(volume, vm, dskCh, pool, size);
cmd = new CreateCommand(dskCh, new StorageFilerTO(pool), size);
}
Answer answer = sendToPool(pool, cmd);
@ -2441,6 +2371,149 @@ public class StorageManagerImpl implements StorageManager {
return createDiskCharacteristics(volume, offering);
}
protected StoragePool findStorage(DiskProfile dskCh, DeployDestination dest, VirtualMachineProfile vm, List<? extends Volume> alreadyAllocated, Set<? extends StoragePool> avoid) {
for (StoragePoolAllocator allocator : _storagePoolAllocators) {
StoragePool pool = allocator.allocateTo(dskCh, vm, dest, alreadyAllocated, avoid);
if (pool != null) {
return pool;
}
}
return null;
}
@Override
public VolumeTO[] prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException {
List<VolumeVO> vols = _volsDao.findUsableVolumesForInstance(vm.getId());
if (s_logger.isDebugEnabled()) {
s_logger.debug("Preparing " + vols.size() + " volumes for " + vm);
}
List<VolumeVO> recreateVols = new ArrayList<VolumeVO>(vols.size());
VolumeTO[] disks = new VolumeTO[vols.size()];
int i = 0;
for (VolumeVO vol : vols) {
Volume.State state = vol.getState();
if (state == Volume.State.Ready) {
StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId());
if (pool.getRemoved() != null || pool.isInMaintenance()) {
if (vol.isRecreatable()) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Volume " + vol + " has to be recreated due to storage pool " + pool + " is unavailable");
}
recreateVols.add(vol);
} else {
throw new StorageUnavailableException("Volume " + vol + " is not available on the storage pool.", pool);
}
} else {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Volume " + vol + " is ready.");
}
disks[i++] = new VolumeTO(vol, pool);
}
} else if (state == Volume.State.Allocated) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Creating volume " + vol + " for the first time.");
}
recreateVols.add(vol);
} else {
throw new StorageUnavailableException("Volume " + vol + " can not be used", vol);
}
}
for (VolumeVO vol : recreateVols) {
VolumeVO newVol;
if (vol.getState() == Volume.State.Allocated) {
newVol = vol;
} else {
newVol = switchVolume(vol);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Created new volume " + newVol + " for old volume " + vol);
}
}
try {
_volsDao.update(newVol, Volume.Event.Create);
} catch(ConcurrentOperationException e) {
throw new StorageUnavailableException("Unable to create " + newVol, newVol);
}
Pair<VolumeTO, StoragePool> created = createVolume(newVol, _diskOfferingDao.findById(newVol.getDiskOfferingId()), vm, vols, dest);
if (created == null) {
newVol.setPoolId(null);
try {
_volsDao.update(newVol, Event.OperationFailed);
} catch (ConcurrentOperationException e) {
throw new CloudRuntimeException("Unable to update the failure on a volume: " + newVol, e);
}
throw new StorageUnavailableException("Unable to create " + newVol, newVol);
}
newVol.setStatus(AsyncInstanceCreateStatus.Created);
newVol.setFolder(created.second().getPath());
newVol.setPath(created.first().getPath());
newVol.setSize(created.first().getSize());
newVol.setPoolType(created.second().getPoolType());
newVol.setPodId(created.second().getPodId());
try {
_volsDao.update(newVol, Event.OperationSucceeded);
} catch (ConcurrentOperationException e) {
throw new CloudRuntimeException("Unable to update an CREATE operation succeeded on volume " + newVol, e);
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Volume " + newVol + " is created on " + created.second());
}
disks[i++] = created.first();
}
return disks;
}
@DB
protected VolumeVO switchVolume(VolumeVO existingVolume) throws StorageUnavailableException {
Transaction txn = Transaction.currentTxn();
try {
txn.start();
_volsDao.update(existingVolume, Event.Destroy);
VolumeVO newVolume = allocateDuplicateVolume(existingVolume);
txn.commit();
return newVolume;
} catch (ConcurrentOperationException e) {
throw new StorageUnavailableException("Unable to duplicate the volume " + existingVolume, existingVolume, e);
}
}
public Pair<VolumeTO, StoragePool> createVolume(VolumeVO toBeCreated, DiskOfferingVO offering, VirtualMachineProfile vm, List<? extends Volume> alreadyCreated, DeployDestination dest) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Creating volume: " + toBeCreated);
}
DiskProfile diskProfile = new DiskProfile(toBeCreated, offering);
Set<StoragePool> avoids = new HashSet<StoragePool>();
StoragePool pool = null;
while ((pool = findStorage(diskProfile, dest, vm, alreadyCreated, avoids)) != null) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Trying to create in " + pool);
}
avoids.add(pool);
toBeCreated.setPoolId(pool.getId());
try {
_volsDao.update(toBeCreated, Volume.Event.OperationRetry);
} catch (ConcurrentOperationException e) {
throw new CloudRuntimeException("Unable to retry a create operation on volume " + toBeCreated);
}
CreateCommand cmd = new CreateCommand(diskProfile, new StorageFilerTO(pool), diskProfile.getSize());
Answer answer = sendToPool(pool, cmd);
if (answer.getResult()) {
CreateAnswer createAnswer = (CreateAnswer)answer;
return new Pair<VolumeTO, StoragePool>(createAnswer.getVolume(), pool);
}
}
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unable to create volume " + toBeCreated);
}
return null;
}
@Override
public <T extends VMInstanceVO> void create(T vm) {
List<VolumeVO> vols = _volsDao.findByInstance(vm.getId());

View File

@ -28,6 +28,8 @@ import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.deploy.DeployDestination;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
@ -229,8 +231,11 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
}
@Override
public StoragePool allocateTo(DiskProfile dskCh, VirtualMachineProfile vm, DeployDestination dest, List<Volume> disks, Set<StoragePool> avoids) {
public StoragePool allocateTo(DiskProfile dskCh, VirtualMachineProfile vm, DeployDestination dest, List<? extends Volume> disks, Set<? extends StoragePool> avoids) {
return null;
VMInstanceVO instance = (VMInstanceVO)(vm.getVm());
VMTemplateVO template = vm.getTemplateId() != null ? _templateDao.findById(vm.getTemplateId()) : null;
return allocateToPool(dskCh, (DataCenterVO)dest.getDataCenter(), (HostPodVO)dest.getPod(), dest.getCluster().getId(), instance, template, avoids);
}
}

View File

@ -43,5 +43,5 @@ public interface StoragePoolAllocator extends Adapter {
String chooseStorageIp(VirtualMachine vm, Host host, Host storage);
StoragePool allocateTo(DiskProfile dskCh, VirtualMachineProfile vm, DeployDestination dest, List<Volume> disks, Set<StoragePool> avoids);
StoragePool allocateTo(DiskProfile dskCh, VirtualMachineProfile vm, DeployDestination dest, List<? extends Volume> disks, Set<? extends StoragePool> avoids);
}

View File

@ -1822,7 +1822,7 @@ public class SecondaryStorageManagerImpl implements SecondaryStorageVmManager, V
if( !_storageMgr.share(secStorageVm, vols, routingHost, false) ) {
s_logger.warn("Can not share " + vol.getPath() + " to " + secStorageVm.getName());
throw new StorageUnavailableException(vol.getPoolId());
throw new StorageUnavailableException("Can not share " + vol.getPath() + " to " + secStorageVm.getName(), vol);
}
Answer answer = _agentMgr.easySend(routingHost.getId(), cmd);

View File

@ -36,7 +36,9 @@ import com.cloud.deploy.DeployDestination;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.network.NetworkConfigurationVO;
import com.cloud.network.NetworkManager;
import com.cloud.offering.ServiceOffering;
@ -233,7 +235,7 @@ public class MauriceMoss implements VmManager {
}
@Override
public <T extends VMInstanceVO> T start(T vm, DeploymentPlan plan) throws InsufficientCapacityException {
public <T extends VMInstanceVO> T start(T vm, DeploymentPlan plan) throws InsufficientCapacityException, ConcurrentOperationException {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Creating actual resources for VM " + vm);
}
@ -246,7 +248,7 @@ public class MauriceMoss implements VmManager {
Set<DeployDestination> avoids = new HashSet<DeployDestination>();
int retry = _retry;
while (retry-- > 0) {
while (retry-- != 0) { // It's != so that it can match -1.
DeployDestination dest = null;
for (DeploymentPlanner dispatcher : _planners) {
dest = dispatcher.plan(vmProfile, plan, avoids);
@ -266,8 +268,15 @@ public class MauriceMoss implements VmManager {
_vmDao.updateIf(vm, Event.StartRequested, dest.getHost().getId());
VirtualMachineTO vmTO = new VirtualMachineTO();
try {
_storageMgr.prepare(vmProfile, dest);
} catch (ConcurrentOperationException e) {
throw e;
} catch (StorageUnavailableException e) {
s_logger.warn("Unable to contact storage.", e);
continue;
}
_networkMgr.prepare(vmProfile, dest);
// _storageMgr.prepare(vm, dest);
}
if (s_logger.isDebugEnabled()) {

View File

@ -179,9 +179,9 @@ import com.cloud.vm.DomainRouter.Role;
import com.cloud.vm.VirtualMachine.Event;
import com.cloud.vm.VirtualMachine.Type;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.InstanceGroupDao;
import com.cloud.vm.dao.InstanceGroupVMMapDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.InstanceGroupDao;
@Local(value={UserVmManager.class})
public class UserVmManagerImpl implements UserVmManager {
@ -1207,6 +1207,7 @@ public class UserVmManagerImpl implements UserVmManager {
}
@Override
public void releaseGuestIpAddress(UserVmVO userVm) {
ServiceOffering offering = _offeringDao.findById(userVm.getServiceOfferingId());
@ -2117,7 +2118,7 @@ public class UserVmManagerImpl implements UserVmManager {
if( !_storageMgr.share(vm, vols, vmHost, false) ) {
s_logger.warn("Can not share " + vm.toString() + " on host " + vmHost.getId());
throw new StorageUnavailableException(vmHost.getId());
throw new StorageUnavailableException("Can not share " + vm.toString() + " on host " + vmHost.getId());
}
Answer answer = _agentMgr.easySend(vmHost.getId(), cmd);
@ -2400,6 +2401,7 @@ public class UserVmManagerImpl implements UserVmManager {
}
}
@Override
public VMTemplateVO createPrivateTemplateRecord(Long userId, long volumeId, String name, String description, long guestOSId, Boolean requiresHvm, Integer bits, Boolean passwordEnabled, boolean isPublic, boolean featured)
throws InvalidParameterValueException {
@ -3014,7 +3016,8 @@ public class UserVmManagerImpl implements UserVmManager {
_vmMgr = vmMgr;
}
public void run() {
@Override
public void run() {
GlobalLock scanLock = GlobalLock.getInternLock("UserVMExpunge");
try {
if(scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) {

View File

@ -52,4 +52,6 @@ public interface FiniteState<S, E> {
* @return array of events.
*/
Set<E> getPossibleEvents();
String getDescription();
}