content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  package org.apache.cloudstack.ha;
  
  import com.cloud.api.response.SimulatorHAStateResponse;
  import com.cloud.exception.InvalidParameterValueException;
  import com.cloud.host.Host;
  import com.cloud.hypervisor.Hypervisor;
  import com.cloud.utils.fsm.StateListener;
  import com.cloud.utils.fsm.StateMachine2;
  import org.apache.cloudstack.ha.provider.HACheckerException;
  import org.apache.cloudstack.ha.provider.HAFenceException;
  import org.apache.cloudstack.ha.provider.HAProvider;
  import org.apache.cloudstack.ha.provider.HARecoveryException;
  import org.apache.cloudstack.ha.provider.host.HAAbstractHostProvider;
  import org.joda.time.DateTime;
  
  import javax.inject.Inject;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  public class SimulatorHAProvider extends HAAbstractHostProvider implements HAProvider<Host>, StateListener<HAConfig.HAState, HAConfig.Event, HAConfig> {
  
      @Inject
      private HAManager haManager;
  
      private final Map<Long, SimulatorHAState> hostHAStateMap = new ConcurrentHashMap<>();
  
      public SimulatorHAProvider() {
          HAConfig.HAState.getStateMachine().registerListener(this);
      }
  
      public void setHAStateForHost(final Long hostId, final SimulatorHAState state) {
          hostHAStateMap.put(hostId, state);
          haManager.purgeHACounter(hostId, HAResource.ResourceType.Host);
      }
  
      public List<SimulatorHAStateResponse> listHAStateTransitions(final Long hostId) {
          final SimulatorHAState haState = hostHAStateMap.get(hostId);
          if (haState == null) {
              return Collections.emptyList();
          }
          return haState.listRecentStateTransitions();
      }
  
      @Override
      public HAResource.ResourceType resourceType() {
          return HAResource.ResourceType.Host;
      }
  
      @Override
      public HAResource.ResourceSubType resourceSubType() {
          return HAResource.ResourceSubType.Simulator;
      }
  
      @Override
      public boolean isEligible(final Host host) {
          if (host == null) {
              return false;
          }
          final SimulatorHAState haState = hostHAStateMap.get(host.getId());
          return !isInMaintenanceMode(host) && !isDisabled(host) && haState != null
                  && Hypervisor.HypervisorType.Simulator.equals(host.getHypervisorType());
      }
  
      @Override
      public boolean isHealthy(final Host host) throws HACheckerException {
          final SimulatorHAState haState = hostHAStateMap.get(host.getId());
          return haState != null && haState.isHealthy();
      }
  
      @Override
      public boolean hasActivity(final Host host, final DateTime afterThis) throws HACheckerException {
          final SimulatorHAState haState = hostHAStateMap.get(host.getId());
          return haState != null && haState.hasActivity();
      }
  
      @Override
      public boolean recover(final Host host) throws HARecoveryException {
          final SimulatorHAState haState = hostHAStateMap.get(host.getId());
          return haState != null && haState.canRecover();
      }
  
      @Override
      public boolean fence(final Host host) throws HAFenceException {
          final SimulatorHAState haState = hostHAStateMap.get(host.getId());
          return haState != null && haState.canFenced();
      }
  
      @Override
      public Object getConfigValue(final HAProvider.HAProviderConfig name, final Host host) {
          switch (name) {
              case HealthCheckTimeout:
                  return 5L;
              case ActivityCheckTimeout:
                  return 5L;
              case RecoveryTimeout:
                  return 5L;
              case FenceTimeout:
                  return 5L;
              case MaxActivityCheckInterval:
                  return 1L;
              case MaxActivityChecks:
                  return 3L;
              case ActivityCheckFailureRatio:
                  final SimulatorHAState haState = hostHAStateMap.get(host.getId());
                  return (haState != null && haState.hasActivity()) ? 1.0 : 0.0;
              case MaxDegradedWaitTimeout:
                  return 1L;
              case MaxRecoveryAttempts:
                  return 2L;
              case RecoveryWaitTimeout:
                  return 1L;
              default:
                  throw new InvalidParameterValueException("Unknown HAProviderConfig " + name.toString());
          }
      }
  
      private boolean addStateTransition(final HAConfig vo, final boolean status,
                                         final HAConfig.HAState oldState, final HAConfig.HAState newState, final HAConfig.Event event) {
          if (vo.getResourceType() != HAResource.ResourceType.Host) {
              return false;
          }
          final SimulatorHAState haState = hostHAStateMap.get(vo.getResourceId());
          if (haState == null || !status) {
              return false;
          }
          final HAResourceCounter counter = haManager.getHACounter(vo.getResourceId(), vo.getResourceType());
          return haState.addStateTransition(newState, oldState, event, counter);
      }
  
      @Override
      public boolean preStateTransitionEvent(final HAConfig.HAState oldState, final HAConfig.Event event,
                                             final HAConfig.HAState newState, final HAConfig vo, final boolean status, final Object opaque) {
          return addStateTransition(vo, status, oldState, newState, event);
      }
  
      @Override
      public boolean postStateTransitionEvent(final StateMachine2.Transition<HAConfig.HAState, HAConfig.Event> transition,
                                              final HAConfig vo, final boolean status, final Object opaque) {
          return addStateTransition(vo, status, transition.getCurrentState(), transition.getToState(), transition.getEvent());
      }
  }
  