From 50185b7c3a1fd26e9a8635c28cee70fda32d3fc5 Mon Sep 17 00:00:00 2001 From: Damodar Date: Thu, 9 Oct 2014 11:52:28 +0530 Subject: [PATCH] CLOUDSTACK-7648: There are new VM State Machine changes introduced which were missed to capture the usage events --- api/src/com/cloud/vm/VirtualMachine.java | 103 +++++++------ .../cloud/network/NetworkStateListener.java | 12 +- .../manager/BaremetalManagerImpl.java | 59 ++++---- .../network/ovs/OvsTunnelManagerImpl.java | 32 ++-- .../cloud/capacity/CapacityManagerImpl.java | 132 ++++++++-------- .../deploy/DeploymentPlanningManagerImpl.java | 25 +-- .../VirtualNetworkApplianceManagerImpl.java | 20 ++- .../security/SecurityGroupManagerImpl.java | 46 +++--- .../listener/SnapshotStateListener.java | 9 +- .../storage/listener/VolumeStateListener.java | 9 +- .../src/com/cloud/vm/UserVmStateListener.java | 57 +++---- .../affinity/AffinityGroupServiceImpl.java | 26 ++-- .../com/cloud/utils/fsm/StateListener.java | 17 +-- .../com/cloud/utils/fsm/StateMachine2.java | 142 ++++++++++++++---- 14 files changed, 399 insertions(+), 290 deletions(-) diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java index 34387ebea8f..99152d65fd1 100755 --- a/api/src/com/cloud/vm/VirtualMachine.java +++ b/api/src/com/cloud/vm/VirtualMachine.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.vm; +import java.util.Arrays; import java.util.Date; import java.util.Map; @@ -26,6 +27,8 @@ import org.apache.cloudstack.api.InternalIdentity; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.fsm.StateMachine2; +import com.cloud.utils.fsm.StateMachine2.Transition; +import com.cloud.utils.fsm.StateMachine2.Transition.Impact; import com.cloud.utils.fsm.StateObject; /** @@ -75,63 +78,63 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I protected static final StateMachine2 s_fsm = new StateMachine2(); static { - s_fsm.addTransition(State.Stopped, VirtualMachine.Event.StartRequested, State.Starting); - s_fsm.addTransition(State.Stopped, VirtualMachine.Event.DestroyRequested, State.Destroyed); - s_fsm.addTransition(State.Stopped, VirtualMachine.Event.StopRequested, State.Stopped); - s_fsm.addTransition(State.Stopped, VirtualMachine.Event.AgentReportStopped, State.Stopped); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.StartRequested, State.Starting, null)); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.DestroyRequested, State.Destroyed, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.StopRequested, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.AgentReportStopped, State.Stopped, null)); // please pay attention about state transition to Error state, there should be only one case (failed in VM // creation process) // that can have such transition - s_fsm.addTransition(State.Stopped, VirtualMachine.Event.OperationFailedToError, State.Error); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.OperationFailedToError, State.Error, Arrays.asList(new Impact[]{Impact.USAGE}))); - 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.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.OperationSucceeded, State.Running); - s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationFailed, State.Stopped); - s_fsm.addTransition(State.Starting, VirtualMachine.Event.AgentReportRunning, State.Running); - s_fsm.addTransition(State.Starting, VirtualMachine.Event.AgentReportStopped, State.Stopped); - s_fsm.addTransition(State.Starting, VirtualMachine.Event.AgentReportShutdowned, State.Stopped); - s_fsm.addTransition(State.Destroyed, VirtualMachine.Event.RecoveryRequested, State.Stopped); - s_fsm.addTransition(State.Destroyed, VirtualMachine.Event.ExpungeOperation, State.Expunging); - s_fsm.addTransition(State.Running, VirtualMachine.Event.MigrationRequested, State.Migrating); - s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportRunning, State.Running); - s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportStopped, State.Stopped); - s_fsm.addTransition(State.Running, VirtualMachine.Event.StopRequested, State.Stopping); - s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportShutdowned, State.Stopped); - s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportMigrated, State.Running); - s_fsm.addTransition(State.Running, VirtualMachine.Event.OperationSucceeded, State.Running); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.MigrationRequested, State.Migrating); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.OperationSucceeded, State.Running); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.OperationFailed, State.Running); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.AgentReportRunning, State.Running); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.AgentReportStopped, State.Stopped); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.AgentReportShutdowned, State.Stopped); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.OperationSucceeded, State.Stopped); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.OperationFailed, State.Running); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.AgentReportRunning, State.Running); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.AgentReportStopped, State.Stopped); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.StopRequested, State.Stopping); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.AgentReportShutdowned, State.Stopped); - s_fsm.addTransition(State.Expunging, VirtualMachine.Event.OperationFailed, State.Expunging); - s_fsm.addTransition(State.Expunging, VirtualMachine.Event.ExpungeOperation, State.Expunging); - s_fsm.addTransition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging); - s_fsm.addTransition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.OperationFailed, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.ExpungeOperation, State.Expunging, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.AgentReportShutdowned, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.StorageMigrationRequested, State.Migrating, null)); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.OperationRetry, State.Starting, null)); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.OperationSucceeded, State.Running, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.OperationFailed, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.AgentReportRunning, State.Running, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.AgentReportStopped, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.AgentReportShutdowned, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Destroyed, VirtualMachine.Event.RecoveryRequested, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Destroyed, VirtualMachine.Event.ExpungeOperation, State.Expunging, null)); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.MigrationRequested, State.Migrating, null)); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.AgentReportRunning, State.Running, null)); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.AgentReportStopped, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.StopRequested, State.Stopping, null)); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.AgentReportShutdowned, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.AgentReportMigrated, State.Running, null)); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.OperationSucceeded, State.Running, null)); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.MigrationRequested, State.Migrating, null)); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.OperationSucceeded, State.Running, null)); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.OperationFailed, State.Running, null)); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.AgentReportRunning, State.Running, null)); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.AgentReportStopped, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.AgentReportShutdowned, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.OperationSucceeded, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.OperationFailed, State.Running, null)); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.AgentReportRunning, State.Running, null)); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.AgentReportStopped, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.StopRequested, State.Stopping, null)); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.AgentReportShutdowned, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Expunging, VirtualMachine.Event.OperationFailed, State.Expunging,null)); + s_fsm.addTransition(new Transition(State.Expunging, VirtualMachine.Event.ExpungeOperation, State.Expunging,null)); + s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging, null)); + s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging, null)); - s_fsm.addTransition(State.Starting, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); - s_fsm.addTransition(State.Stopped, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); - s_fsm.addTransition(State.Running, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, null)); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, null)); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, null)); - s_fsm.addTransition(State.Starting, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); - s_fsm.addTransition(State.Stopping, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); - s_fsm.addTransition(State.Running, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); - s_fsm.addTransition(State.Migrating, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); - s_fsm.addTransition(State.Stopped, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); + s_fsm.addTransition(new Transition(State.Starting, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Stopping, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Running, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped, Arrays.asList(new Impact[]{Impact.USAGE}))); + s_fsm.addTransition(new Transition(State.Migrating, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped, null)); + s_fsm.addTransition(new Transition(State.Stopped, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped, null)); } public static boolean isVmStarted(State oldState, Event e, State newState) { diff --git a/engine/components-api/src/com/cloud/network/NetworkStateListener.java b/engine/components-api/src/com/cloud/network/NetworkStateListener.java index c86f7820a5d..0ed1d9ea7fd 100644 --- a/engine/components-api/src/com/cloud/network/NetworkStateListener.java +++ b/engine/components-api/src/com/cloud/network/NetworkStateListener.java @@ -24,6 +24,7 @@ import java.util.Map; import javax.inject.Inject; +import com.cloud.utils.fsm.StateMachine2; import org.apache.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -65,12 +66,15 @@ public class NetworkStateListener implements StateListener transition, Network vo, boolean status, Object opaque) { + State oldState = transition.getCurrentState(); + State newState = transition.getToState(); + Event event = transition.getEvent(); + pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState); + return true; } - private void pubishOnEventBus(String event, String status, Network vo, State oldState, State newState) { + private void pubishOnEventBus(String event, String status, Network vo, State oldState, State newState) { String configKey = "publish.resource.state.events"; String value = _configDao.getValue(configKey); diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java index 92163ea614f..f826ae91fde 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BaremetalManagerImpl.java @@ -29,6 +29,7 @@ import javax.naming.ConfigurationException; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.api.BaremetalProvisionDoneNotificationCmd; @@ -81,37 +82,39 @@ public class BaremetalManagerImpl extends ManagerBase implements BaremetalManage } @Override - 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) { - return true; - } - - if (vo.getHypervisorType() != HypervisorType.BareMetal) { - return true; - } - - HostVO host = _hostDao.findById(vo.getHostId()); - if (host == null) { - s_logger.debug("Skip oldState " + oldState + " to " + "newState " + newState + " transimtion"); - return true; - } - _hostDao.loadDetails(host); - - if (newState == State.Starting) { - host.setDetail("vmName", vo.getInstanceName()); - s_logger.debug("Add vmName " + host.getDetail("vmName") + " to host " + host.getId() + " details"); - } else { - if (host.getDetail("vmName") != null && host.getDetail("vmName").equalsIgnoreCase(vo.getInstanceName())) { - s_logger.debug("Remove vmName " + host.getDetail("vmName") + " from host " + host.getId() + " details"); - host.getDetails().remove("vmName"); - } - } - _hostDao.saveDetails(host); - + public boolean postStateTransitionEvent(StateMachine2.Transition transition, VirtualMachine vo, boolean status, Object opaque) { + State newState = transition.getToState(); + State oldState = transition.getCurrentState(); + if (newState != State.Starting && newState != State.Error && newState != State.Expunging) { return true; + } + + if (vo.getHypervisorType() != HypervisorType.BareMetal) { + return true; + } + + HostVO host = _hostDao.findById(vo.getHostId()); + if (host == null) { + s_logger.debug("Skip oldState " + oldState + " to " + "newState " + newState + " transimtion"); + return true; + } + _hostDao.loadDetails(host); + + if (newState == State.Starting) { + host.setDetail("vmName", vo.getInstanceName()); + s_logger.debug("Add vmName " + host.getDetail("vmName") + " to host " + host.getId() + " details"); + } else { + if (host.getDetail("vmName") != null && host.getDetail("vmName").equalsIgnoreCase(vo.getInstanceName())) { + s_logger.debug("Remove vmName " + host.getDetail("vmName") + " from host " + host.getId() + " details"); + host.getDetails().remove("vmName"); + } + } + _hostDao.saveDetails(host); + + return true; } - @Override + @Override public List> getCommands() { List> cmds = new ArrayList>(); cmds.add(AddBaremetalHostCmd.class); diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index c998e3be38f..04d21fd354b 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -36,6 +36,7 @@ import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.VpcManager; import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.dao.NetworkACLDao; +import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.Nic; @@ -680,25 +681,26 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } @Override - public boolean postStateTransitionEvent(VirtualMachine.State oldState, VirtualMachine.Event event, - VirtualMachine.State newState, VirtualMachine vm, - boolean status, Object opaque) { - if (!status) { - return false; - } + public boolean postStateTransitionEvent(StateMachine2.Transition transition, VirtualMachine vm, boolean status, Object opaque) { + if (!status) { + return false; + } - if (VirtualMachine.State.isVmStarted(oldState, event, newState)) { - handleVmStateChange((VMInstanceVO)vm); - } else if (VirtualMachine.State.isVmStopped(oldState, event, newState)) { - handleVmStateChange((VMInstanceVO)vm); - } else if (VirtualMachine.State.isVmMigrated(oldState, event, newState)) { - handleVmStateChange((VMInstanceVO)vm); - } + VirtualMachine.State oldState = transition.getCurrentState(); + VirtualMachine.State newState = transition.getToState(); + VirtualMachine.Event event = transition.getEvent(); + if (VirtualMachine.State.isVmStarted(oldState, event, newState)) { + handleVmStateChange((VMInstanceVO)vm); + } else if (VirtualMachine.State.isVmStopped(oldState, event, newState)) { + handleVmStateChange((VMInstanceVO)vm); + } else if (VirtualMachine.State.isVmMigrated(oldState, event, newState)) { + handleVmStateChange((VMInstanceVO)vm); + } - return true; + return true; } - private void handleVmStateChange(VMInstanceVO vm) { + private void handleVmStateChange(VMInstanceVO vm) { // get the VPC's impacted with the VM start List vpcIds = _ovsNetworkToplogyGuru.getVpcIdsVmIsPartOf(vm.getId()); diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index e5b7d191e2c..68664440b82 100755 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -27,6 +27,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.fsm.StateMachine2; import org.apache.log4j.Logger; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; @@ -770,79 +771,82 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, } @Override - public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Object opaque) { - if (!status) { - return false; + public boolean postStateTransitionEvent(StateMachine2.Transition transition, VirtualMachine vm, boolean status, Object opaque) { + if (!status) { + return false; + } + @SuppressWarnings("unchecked") + Pair hosts = (Pair)opaque; + Long oldHostId = hosts.first(); + + State oldState = transition.getCurrentState(); + State newState = transition.getToState(); + Event event = transition.getEvent(); + 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); + + if (oldState == State.Starting) { + if (newState != State.Running) { + releaseVmCapacity(vm, false, false, oldHostId); } - @SuppressWarnings("unchecked") - Pair hosts = (Pair)opaque; - Long oldHostId = hosts.first(); - - 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); - - if (oldState == State.Starting) { - if (newState != State.Running) { - releaseVmCapacity(vm, false, false, oldHostId); - } - } else if (oldState == State.Running) { - if (event == Event.AgentReportStopped) { - releaseVmCapacity(vm, false, true, oldHostId); - } else if (event == Event.AgentReportMigrated) { - releaseVmCapacity(vm, false, false, oldHostId); - } - } else if (oldState == State.Migrating) { - if (event == Event.AgentReportStopped) { + } else if (oldState == State.Running) { + if (event == Event.AgentReportStopped) { + releaseVmCapacity(vm, false, true, oldHostId); + } else if (event == Event.AgentReportMigrated) { + releaseVmCapacity(vm, false, false, oldHostId); + } + } else if (oldState == State.Migrating) { + if (event == Event.AgentReportStopped) { /* Release capacity from original host */ - releaseVmCapacity(vm, false, false, vm.getLastHostId()); - releaseVmCapacity(vm, false, false, oldHostId); - } else if (event == Event.OperationFailed) { + releaseVmCapacity(vm, false, false, vm.getLastHostId()); + releaseVmCapacity(vm, false, false, oldHostId); + } else if (event == Event.OperationFailed) { /* Release from dest host */ - releaseVmCapacity(vm, false, false, oldHostId); - } else if (event == Event.OperationSucceeded) { - releaseVmCapacity(vm, false, false, vm.getLastHostId()); - } - } else if (oldState == State.Stopping) { - if (event == Event.OperationSucceeded) { - releaseVmCapacity(vm, false, true, oldHostId); - } else if (event == Event.AgentReportStopped) { - releaseVmCapacity(vm, false, false, oldHostId); - } else if (event == Event.AgentReportMigrated) { - releaseVmCapacity(vm, false, false, oldHostId); - } - } else if (oldState == State.Stopped) { - if (event == Event.DestroyRequested || event == Event.ExpungeOperation) { - releaseVmCapacity(vm, true, false, vm.getLastHostId()); - } else if (event == Event.AgentReportMigrated) { - releaseVmCapacity(vm, false, false, oldHostId); - } + releaseVmCapacity(vm, false, false, oldHostId); + } else if (event == Event.OperationSucceeded) { + releaseVmCapacity(vm, false, false, vm.getLastHostId()); } - - if ((newState == State.Starting || newState == State.Migrating || event == Event.AgentReportMigrated) && vm.getHostId() != null) { - boolean fromLastHost = false; - if (vm.getHostId().equals(vm.getLastHostId())) { - s_logger.debug("VM starting again on the last host it was stopped on"); - fromLastHost = true; - } - allocateVmCapacity(vm, fromLastHost); + } else if (oldState == State.Stopping) { + if (event == Event.OperationSucceeded) { + releaseVmCapacity(vm, false, true, oldHostId); + } else if (event == Event.AgentReportStopped) { + releaseVmCapacity(vm, false, false, oldHostId); + } else if (event == Event.AgentReportMigrated) { + releaseVmCapacity(vm, false, false, oldHostId); } - - if (newState == State.Stopped) { - if (vm.getType() == VirtualMachine.Type.User) { - - UserVmVO userVM = _userVMDao.findById(vm.getId()); - _userVMDao.loadDetails(userVM); - // free the message sent flag if it exists - userVM.setDetail(MESSAGE_RESERVED_CAPACITY_FREED_FLAG, "false"); - _userVMDao.saveDetails(userVM); - - } + } else if (oldState == State.Stopped) { + if (event == Event.DestroyRequested || event == Event.ExpungeOperation) { + releaseVmCapacity(vm, true, false, vm.getLastHostId()); + } else if (event == Event.AgentReportMigrated) { + releaseVmCapacity(vm, false, false, oldHostId); } + } - return true; + if ((newState == State.Starting || newState == State.Migrating || event == Event.AgentReportMigrated) && vm.getHostId() != null) { + boolean fromLastHost = false; + if (vm.getHostId().equals(vm.getLastHostId())) { + s_logger.debug("VM starting again on the last host it was stopped on"); + fromLastHost = true; + } + allocateVmCapacity(vm, fromLastHost); + } + + if (newState == State.Stopped) { + if (vm.getType() == VirtualMachine.Type.User) { + + UserVmVO userVM = _userVMDao.findById(vm.getId()); + _userVMDao.loadDetails(userVM); + // free the message sent flag if it exists + userVM.setDetail(MESSAGE_RESERVED_CAPACITY_FREED_FLAG, "false"); + _userVMDao.saveDetails(userVM); + + } + } + + return true; } - // TODO: Get rid of this case once we've determined that the capacity listeners above have all the changes + // TODO: Get rid of this case once we've determined that the capacity listeners above have all the changes // create capacity entries if none exist for this server private void createCapacityEntry(StartupCommand startup, HostVO server) { SearchCriteria capacitySC = _capacityDao.createSearchCriteria(); diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index a40a7d71f0d..bfb33b0c624 100755 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -31,6 +31,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.fsm.StateMachine2; import org.apache.log4j.Logger; import org.apache.cloudstack.affinity.AffinityGroupProcessor; @@ -1443,16 +1444,18 @@ StateListener { } @Override - public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { - if (!status) { - return false; - } - if ((oldState == State.Starting) && (newState != State.Starting)) { - // cleanup all VM reservation entries - SearchCriteria sc = _reservationDao.createSearchCriteria(); - sc.addAnd("vmId", SearchCriteria.Op.EQ, vo.getId()); - _reservationDao.expunge(sc); - } - return true; + public boolean postStateTransitionEvent(StateMachine2.Transition transition, VirtualMachine vo, boolean status, Object opaque) { + if (!status) { + return false; + } + State oldState = transition.getCurrentState(); + State newState = transition.getToState(); + if ((oldState == State.Starting) && (newState != State.Starting)) { + // cleanup all VM reservation entries + SearchCriteria sc = _reservationDao.createSearchCriteria(); + sc.addAnd("vmId", SearchCriteria.Op.EQ, vo.getId()); + _reservationDao.expunge(sc); + } + return true; } } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 968c5380ecd..7d1dfa4aeec 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -218,6 +218,7 @@ import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.StateListener; +import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.net.Ip; import com.cloud.utils.net.MacAddress; import com.cloud.utils.net.NetUtils; @@ -4432,17 +4433,20 @@ VirtualMachineGuru, Listener, Configurable, StateListener transition, VirtualMachine vo, boolean status, Object opaque) { + State oldState = transition.getCurrentState(); + State newState = transition.getToState(); + VirtualMachine.Event event = transition.getEvent(); + if (oldState == State.Stopped && event == VirtualMachine.Event.FollowAgentPowerOnReport && newState == State.Running) { + if (vo.getType() == VirtualMachine.Type.DomainRouter) { + s_logger.info("Schedule a router reboot task as router " + vo.getId() + " is powered-on out-of-band. we need to reboot to refresh network rules"); + _executor.schedule(new RebootTask(vo.getId()), 1000, TimeUnit.MICROSECONDS); } - return true; + } + return true; } - protected class RebootTask extends ManagedContextRunnable { + protected class RebootTask extends ManagedContextRunnable { long _routerId; diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index f60a746e68c..cffdf8f8be2 100755 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -40,6 +40,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.fsm.StateMachine2; import org.apache.commons.codec.digest.DigestUtils; import org.apache.log4j.Logger; @@ -1279,32 +1280,35 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro } @Override - public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Object opaque) { - if (!status) { - return false; - } + public boolean postStateTransitionEvent(StateMachine2.Transition transition, VirtualMachine vm, boolean status, Object opaque) { + if (!status) { + return false; + } - if (VirtualMachine.State.isVmStarted(oldState, event, newState)) { - if (s_logger.isTraceEnabled()) { - s_logger.trace("Security Group Mgr: handling start of vm id" + vm.getId()); - } - handleVmStarted((VMInstanceVO)vm); - } else if (VirtualMachine.State.isVmStopped(oldState, event, newState)) { - if (s_logger.isTraceEnabled()) { - s_logger.trace("Security Group Mgr: handling stop of vm id" + vm.getId()); - } - handleVmStopped((VMInstanceVO)vm); - } else if (VirtualMachine.State.isVmMigrated(oldState, event, newState)) { - if (s_logger.isTraceEnabled()) { - s_logger.trace("Security Group Mgr: handling migration of vm id" + vm.getId()); - } - handleVmMigrated((VMInstanceVO)vm); + State oldState = transition.getCurrentState(); + State newState = transition.getToState(); + Event event = transition.getEvent(); + if (VirtualMachine.State.isVmStarted(oldState, event, newState)) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Security Group Mgr: handling start of vm id" + vm.getId()); } + handleVmStarted((VMInstanceVO)vm); + } else if (VirtualMachine.State.isVmStopped(oldState, event, newState)) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Security Group Mgr: handling stop of vm id" + vm.getId()); + } + handleVmStopped((VMInstanceVO)vm); + } else if (VirtualMachine.State.isVmMigrated(oldState, event, newState)) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Security Group Mgr: handling migration of vm id" + vm.getId()); + } + handleVmMigrated((VMInstanceVO)vm); + } - return true; + return true; } - @Override + @Override public boolean isVmSecurityGroupEnabled(Long vmId) { VirtualMachine vm = _vmDao.findByIdIncludingRemoved(vmId); List nics = _networkMgr.getNicProfiles(vm); diff --git a/server/src/com/cloud/storage/listener/SnapshotStateListener.java b/server/src/com/cloud/storage/listener/SnapshotStateListener.java index 8da71a6793c..f4decf6f058 100644 --- a/server/src/com/cloud/storage/listener/SnapshotStateListener.java +++ b/server/src/com/cloud/storage/listener/SnapshotStateListener.java @@ -26,6 +26,7 @@ import javax.annotation.PostConstruct; import javax.ejb.Local; import javax.inject.Inject; +import com.cloud.utils.fsm.StateMachine2; import org.apache.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.stereotype.Component; @@ -72,12 +73,12 @@ public class SnapshotStateListener implements StateListener transition, SnapshotVO vo, boolean status, Object opaque) { + pubishOnEventBus(transition.getEvent().name(), "postStateTransitionEvent", vo, transition.getCurrentState(), transition.getToState()); + return true; } - private void pubishOnEventBus(String event, String status, Snapshot vo, State oldState, State newState) { + private void pubishOnEventBus(String event, String status, Snapshot vo, State oldState, State newState) { String configKey = Config.PublishResourceStateEvent.key(); String value = s_configDao.getValue(configKey); diff --git a/server/src/com/cloud/storage/listener/VolumeStateListener.java b/server/src/com/cloud/storage/listener/VolumeStateListener.java index 1911a482931..0ba2969a76a 100644 --- a/server/src/com/cloud/storage/listener/VolumeStateListener.java +++ b/server/src/com/cloud/storage/listener/VolumeStateListener.java @@ -22,6 +22,7 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; +import com.cloud.utils.fsm.StateMachine2; import org.apache.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -56,12 +57,12 @@ public class VolumeStateListener implements StateListener } @Override - public boolean postStateTransitionEvent(State oldState, Event event, State newState, Volume vo, boolean status, Object opaque) { - pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState); - return true; + public boolean postStateTransitionEvent(StateMachine2.Transition transition, Volume vo, boolean status, Object opaque) { + pubishOnEventBus(transition.getEvent().name(), "postStateTransitionEvent", vo, transition.getCurrentState(), transition.getToState()); + return true; } - private void pubishOnEventBus(String event, String status, Volume vo, State oldState, State newState) { + private void pubishOnEventBus(String event, String status, Volume vo, State oldState, State newState) { String configKey = Config.PublishResourceStateEvent.key(); String value = _configDao.getValue(configKey); diff --git a/server/src/com/cloud/vm/UserVmStateListener.java b/server/src/com/cloud/vm/UserVmStateListener.java index a0088b87163..e4df6bbbeb8 100644 --- a/server/src/com/cloud/vm/UserVmStateListener.java +++ b/server/src/com/cloud/vm/UserVmStateListener.java @@ -25,6 +25,7 @@ import java.util.Map; import javax.inject.Inject; import com.cloud.server.ManagementService; +import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.dao.UserVmDao; import org.apache.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -77,36 +78,40 @@ public class UserVmStateListener implements StateListener transition, VirtualMachine vo, boolean status, Object opaque) { + if (!status) { + return false; + } + Event event = transition.getEvent(); + State oldState = transition.getCurrentState(); + State newState = transition.getToState(); + pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState); - pubishOnEventBus(event.name(), "postStateTransitionEvent", vo, oldState, newState); - - if (vo.getType() != VirtualMachine.Type.User) { - return true; - } - - if (VirtualMachine.State.isVmCreated(oldState, event, newState)) { - generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_CREATE); - } else if (VirtualMachine.State.isVmStarted(oldState, event, newState)) { - generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_START); - } else if (VirtualMachine.State.isVmStopped(oldState, event, newState)) { - generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_STOP); - List nics = _nicDao.listByVmId(vo.getId()); - for (NicVO nic : nics) { - NetworkVO network = _networkDao.findById(nic.getNetworkId()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vo.getAccountId(), vo.getDataCenterId(), vo.getId(), - Long.toString(nic.getId()), network.getNetworkOfferingId(), null, 0L, vo.getClass().getName(), vo.getUuid(), vo.isDisplay()); - } - } else if (VirtualMachine.State.isVmDestroyed(oldState, event, newState)) { - generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_DESTROY); - } + if (vo.getType() != VirtualMachine.Type.User) { return true; + } + + if(transition.isImpacted(StateMachine2.Transition.Impact.USAGE)) { + if (oldState == State.Destroyed && newState == State.Stopped) { + generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_CREATE); + } else if (newState == State.Running) { + generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_START); + } else if (newState == State.Stopped) { + generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_STOP); + List nics = _nicDao.listByVmId(vo.getId()); + for (NicVO nic : nics) { + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vo.getAccountId(), vo.getDataCenterId(), vo.getId(), + Long.toString(nic.getId()), network.getNetworkOfferingId(), null, 0L, vo.getClass().getName(), vo.getUuid(), vo.isDisplay()); + } + } else if (newState == State.Destroyed || newState == State.Error || newState == State.Expunging) { + generateUsageEvent(vo.getServiceOfferingId(), vo, EventTypes.EVENT_VM_DESTROY); + } + } + return true; } - private void generateUsageEvent(Long serviceOfferingId, VirtualMachine vm, String eventType){ + private void generateUsageEvent(Long serviceOfferingId, VirtualMachine vm, String eventType){ boolean displayVm = true; if(vm.getType() == VirtualMachine.Type.User){ UserVmVO uservm = _userVmDao.findById(vm.getId()); diff --git a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java index 592ebd6b81c..b984b970060 100644 --- a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java +++ b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java @@ -26,6 +26,7 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.fsm.StateMachine2; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.ControlledEntity; @@ -439,20 +440,21 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro } @Override - public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { - if (!status) { - return false; - } - if ((newState == State.Expunging) || (newState == State.Error)) { - // cleanup all affinity groups associations of the Expunged VM - SearchCriteria sc = _affinityGroupVMMapDao.createSearchCriteria(); - sc.addAnd("instanceId", SearchCriteria.Op.EQ, vo.getId()); - _affinityGroupVMMapDao.expunge(sc); - } - return true; + public boolean postStateTransitionEvent(StateMachine2.Transition transition, VirtualMachine vo, boolean status, Object opaque) { + if (!status) { + return false; + } + State newState = transition.getToState(); + if ((newState == State.Expunging) || (newState == State.Error)) { + // cleanup all affinity groups associations of the Expunged VM + SearchCriteria sc = _affinityGroupVMMapDao.createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, vo.getId()); + _affinityGroupVMMapDao.expunge(sc); + } + return true; } - @Override + @Override public UserVm updateVMAffinityGroups(Long vmId, List affinityGroupIds) { // Verify input parameters UserVmVO vmInstance = _userVmDao.findById(vmId); diff --git a/utils/src/com/cloud/utils/fsm/StateListener.java b/utils/src/com/cloud/utils/fsm/StateListener.java index 3d0a645664a..96e8be92c3c 100644 --- a/utils/src/com/cloud/utils/fsm/StateListener.java +++ b/utils/src/com/cloud/utils/fsm/StateListener.java @@ -28,19 +28,16 @@ public interface StateListener { * @param newState VM's new state * @param vo the VM instance * @param opaque host id - * @param vmDao VM dao * @return */ public boolean preStateTransitionEvent(S oldState, E event, S newState, V vo, boolean status, Object opaque); /** - * Event is triggered after state machine transition finished - * @param oldState VM's old state - * @param event that triggered this VM state change - * @param newState VM's new state - * @param vo the VM instance - * @param status the state transition is allowed or not - * @return - */ - public boolean postStateTransitionEvent(S oldState, E event, S newState, V vo, boolean status, Object opaque); + * Event is triggered after state machine transition finished + * @param transition The Transition fo the Event + * @param vo the VM instance + * @param status the state transition is allowed or not + * @return + */ + public boolean postStateTransitionEvent(StateMachine2.Transition transition, V vo, boolean status, Object opaque); } diff --git a/utils/src/com/cloud/utils/fsm/StateMachine2.java b/utils/src/com/cloud/utils/fsm/StateMachine2.java index 4950a25cb91..431d961a416 100644 --- a/utils/src/com/cloud/utils/fsm/StateMachine2.java +++ b/utils/src/com/cloud/utils/fsm/StateMachine2.java @@ -46,25 +46,33 @@ public class StateMachine2> { } public void addTransition(S currentState, E event, S toState) { - StateEntry entry = null; - if (currentState == null) { - entry = _initialStateEntry; - } else { - entry = _states.get(currentState); - if (entry == null) { - entry = new StateEntry(currentState); - _states.put(currentState, entry); - } - } + addTransition(new Transition(currentState, event, toState, null)); + } - entry.addTransition(event, toState); - entry = _states.get(toState); + public void addTransition(Transition transition) { + S currentState = transition.getCurrentState(); + E event = transition.getEvent(); + S toState = transition.getToState(); + StateEntry entry = null; + if (currentState == null) { + entry = _initialStateEntry; + } else { + entry = _states.get(currentState); if (entry == null) { - entry = new StateEntry(toState); - _states.put(toState, entry); + entry = new StateEntry(currentState); + _states.put(currentState, entry); } - entry.addFromTransition(event, currentState); + } + + entry.addTransition(event, toState, transition); + + entry = _states.get(toState); + if (entry == null) { + entry = new StateEntry(toState); + _states.put(toState, entry); + } + entry.addFromTransition(event, currentState); } public Set getPossibleEvents(S s) { @@ -73,19 +81,23 @@ public class StateMachine2> { } public S getNextState(S s, E e) throws NoTransitionException { - StateEntry entry = null; - if (s == null) { - entry = _initialStateEntry; - } else { - entry = _states.get(s); - assert entry != null : "Cannot retrieve transitions for state " + s; - } + return getTransition(s, e).getToState(); + } - S ns = entry.nextStates.get(e); - if (ns == null) { - throw new NoTransitionException("Unable to transition to a new state from " + s + " via " + e); - } - return ns; + public Transition getTransition(S s, E e) throws NoTransitionException { + StateEntry entry = null; + if (s == null) { + entry = _initialStateEntry; + } else { + entry = _states.get(s); + assert entry != null : "Cannot retrieve transitions for state " + s; + } + + Transition transition = entry.nextStates.get(e); + if (transition == null) { + throw new NoTransitionException("Unable to transition to a new state from " + s + " via " + e); + } + return transition; } public List getFromStates(S s, E e) { @@ -100,6 +112,7 @@ public class StateMachine2> { public boolean transitTo(V vo, E e, Object opaque, StateDao dao) throws NoTransitionException { S currentState = vo.getState(); S nextState = getNextState(currentState, e); + Transition transition = getTransition(currentState, e); boolean transitionStatus = true; if (nextState == null) { @@ -116,7 +129,7 @@ public class StateMachine2> { } for (StateListener listener : _listeners) { - listener.postStateTransitionEvent(currentState, e, nextState, vo, transitionStatus, opaque); + listener.postStateTransitionEvent(transition, vo, transitionStatus, opaque); } return true; @@ -138,21 +151,84 @@ public class StateMachine2> { return str.toString(); } + public static class Transition { + + private S currentState; + + private E event; + + private S toState; + + private List impacts; + + public static enum Impact { + USAGE + } + + public Transition(S currentState, E event, S toState, List impacts) { + this.currentState = currentState; + this.event = event; + this.toState = toState; + this.impacts = impacts; + } + + public S getCurrentState() { + return currentState; + } + + public E getEvent() { + return event; + } + + public S getToState() { + return toState; + } + + public boolean isImpacted(Impact impact) { + if (impacts == null || impacts.isEmpty()) { + return false; + } + return impacts.contains(impact); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Transition that = (Transition) o; + + if (currentState != null ? !currentState.equals(that.currentState) : that.currentState != null) return false; + if (event != null ? !event.equals(that.event) : that.event != null) return false; + if (toState != null ? !toState.equals(that.toState) : that.toState != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = currentState != null ? currentState.hashCode() : 0; + result = 31 * result + (event != null ? event.hashCode() : 0); + result = 31 * result + (toState != null ? toState.hashCode() : 0); + return result; + } + } + private class StateEntry { public S state; - public HashMap nextStates; + public HashMap> nextStates; public HashMap> prevStates; public StateEntry(S state) { this.state = state; - nextStates = new HashMap(); prevStates = new HashMap>(); + nextStates = new HashMap>(); } - public void addTransition(E e, S s) { + public void addTransition(E e, S s, Transition transition) { assert !nextStates.containsKey(e) : "State " + getStateStr() + " already contains a transition to state " + nextStates.get(e).toString() + " via event " + e.toString() + ". Please revisit the rule you're adding to state " + s.toString(); - nextStates.put(e, s); + nextStates.put(e, transition); } public void addFromTransition(E e, S s) { @@ -172,7 +248,7 @@ public class StateMachine2> { public void buildString(StringBuilder str) { str.append("State: ").append(getStateStr()).append("\n"); - for (Map.Entry nextState : nextStates.entrySet()) { + for (Map.Entry> nextState : nextStates.entrySet()) { str.append(" --> Event: "); Formatter format = new Formatter(); str.append(format.format("%-30s", nextState.getKey().toString()));