Package org.apache.ambari.server.controller.internal

Source Code of org.apache.ambari.server.controller.internal.ServiceResourceProvider$HDFSServiceState

/**
* 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.ambari.server.controller.internal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.inject.Inject;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.ClusterNotFoundException;
import org.apache.ambari.server.DuplicateResourceException;
import org.apache.ambari.server.ObjectNotFoundException;
import org.apache.ambari.server.ParentObjectNotFoundException;
import org.apache.ambari.server.ServiceNotFoundException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.MaintenanceStateHelper;
import org.apache.ambari.server.controller.RequestStatusResponse;
import org.apache.ambari.server.controller.ServiceComponentHostRequest;
import org.apache.ambari.server.controller.ServiceComponentHostResponse;
import org.apache.ambari.server.controller.ServiceRequest;
import org.apache.ambari.server.controller.ServiceResponse;
import org.apache.ambari.server.controller.predicate.AndPredicate;
import org.apache.ambari.server.controller.predicate.EqualsPredicate;
import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
import org.apache.ambari.server.controller.spi.NoSuchResourceException;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.RequestStatus;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.ComponentInfo;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.MaintenanceState;
import org.apache.ambari.server.state.Service;
import org.apache.ambari.server.state.ServiceComponent;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.apache.ambari.server.state.ServiceFactory;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.state.State;
import org.apache.commons.lang.StringUtils;

import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import com.google.inject.persist.Transactional;

/**
* Resource provider for service resources.
*/
public class ServiceResourceProvider extends AbstractControllerResourceProvider {


  // ----- Property ID constants ---------------------------------------------

  // Services
  protected static final String SERVICE_CLUSTER_NAME_PROPERTY_ID    = PropertyHelper.getPropertyId("ServiceInfo", "cluster_name");
  protected static final String SERVICE_SERVICE_NAME_PROPERTY_ID    = PropertyHelper.getPropertyId("ServiceInfo", "service_name");
  protected static final String SERVICE_SERVICE_STATE_PROPERTY_ID   = PropertyHelper.getPropertyId("ServiceInfo", "state");
  protected static final String SERVICE_MAINTENANCE_STATE_PROPERTY_ID = PropertyHelper.getPropertyId("ServiceInfo", "maintenance_state");

  //Parameters from the predicate
  private static final String QUERY_PARAMETERS_RUN_SMOKE_TEST_ID =
    "params/run_smoke_test";

  private static final String QUERY_PARAMETERS_RECONFIGURE_CLIENT =
    "params/reconfigure_client";

  private static Set<String> pkPropertyIds =
      new HashSet<String>(Arrays.asList(new String[]{
          SERVICE_CLUSTER_NAME_PROPERTY_ID,
          SERVICE_SERVICE_NAME_PROPERTY_ID}));

  // Service state calculation
  private static final Map<String, ServiceState> serviceStateMap = new HashMap<String, ServiceState>();
  static {
    serviceStateMap.put("HDFS", new HDFSServiceState());
    serviceStateMap.put("HBASE", new HBaseServiceState());
  }

  private static final ServiceState DEFAULT_SERVICE_STATE = new DefaultServiceState();

  @Inject
  private MaintenanceStateHelper maintenanceStateHelper;

  // ----- Constructors ----------------------------------------------------

  /**
   * Create a  new resource provider for the given management controller.
   *
   * @param propertyIds           the property ids
   * @param keyPropertyIds        the key property ids
   * @param managementController  the management controller
   */
  @AssistedInject
  public ServiceResourceProvider(@Assisted Set<String> propertyIds,
                          @Assisted Map<Resource.Type, String> keyPropertyIds,
                          @Assisted AmbariManagementController managementController) {
    super(propertyIds, keyPropertyIds, managementController);
  }

  // ----- ResourceProvider ------------------------------------------------

  @Override
  public RequestStatus createResources(Request request)
      throws SystemException,
             UnsupportedPropertyException,
             ResourceAlreadyExistsException,
             NoSuchParentResourceException {

    final Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
    for (Map<String, Object> propertyMap : request.getProperties()) {
      requests.add(getRequest(propertyMap));
    }
    createResources(new Command<Void>() {
      @Override
      public Void invoke() throws AmbariException {
        createServices(requests);
        return null;
      }
    });
    notifyCreate(Resource.Type.Service, request);

    return getRequestStatus(null);
  }

  @Override
  @Transactional
  public Set<Resource> getResources(Request request, Predicate predicate) throws
      SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {

    final Set<ServiceRequest> requests = new HashSet<ServiceRequest>();

    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
      requests.add(getRequest(propertyMap));
    }

    Set<ServiceResponse> responses = getResources(new Command<Set<ServiceResponse>>() {
      @Override
      public Set<ServiceResponse> invoke() throws AmbariException {
        return getServices(requests);
      }
    });

    Set<String>   requestedIds = getRequestPropertyIds(request, predicate);
    Set<Resource> resources    = new HashSet<Resource>();

    for (ServiceResponse response : responses) {
      Resource resource = new ResourceImpl(Resource.Type.Service);
      setResourceProperty(resource, SERVICE_CLUSTER_NAME_PROPERTY_ID,
          response.getClusterName(), requestedIds);
      setResourceProperty(resource, SERVICE_SERVICE_NAME_PROPERTY_ID,
          response.getServiceName(), requestedIds);
      setResourceProperty(resource, SERVICE_SERVICE_STATE_PROPERTY_ID,
          calculateServiceState(response.getClusterName(), response.getServiceName()),
          requestedIds);
      setResourceProperty(resource, SERVICE_MAINTENANCE_STATE_PROPERTY_ID,
          response.getMaintenanceState(), requestedIds);
      resources.add(resource);
    }
    return resources;
  }

  @Override
  public RequestStatus updateResources(final Request request, Predicate predicate)
      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {

    RequestStageContainer requestStages = doUpdateResources(null, request, predicate);

    RequestStatusResponse response = null;
    if (requestStages != null) {
      requestStages.persist();
      response = requestStages.getRequestStatusResponse();
    }
    notifyUpdate(Resource.Type.Service, request, predicate);

    return getRequestStatus(response);
  }

  @Override
  public RequestStatus deleteResources(Predicate predicate)
      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {

    final Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
      requests.add(getRequest(propertyMap));
    }
    RequestStatusResponse response = modifyResources(new Command<RequestStatusResponse>() {
      @Override
      public RequestStatusResponse invoke() throws AmbariException {
        return deleteServices(requests);
      }
    });

    notifyDelete(Resource.Type.Service, predicate);
    return getRequestStatus(response);
  }

  @Override
  public Set<String> checkPropertyIds(Set<String> propertyIds) {
    propertyIds = super.checkPropertyIds(propertyIds);

    if (propertyIds.isEmpty()) {
      return propertyIds;
    }
    Set<String> unsupportedProperties = new HashSet<String>();

    for (String propertyId : propertyIds) {
      if (!propertyId.equals("config")) {
        String propertyCategory = PropertyHelper.getPropertyCategory(propertyId);
        if (propertyCategory == null || !propertyCategory.equals("config")) {
          unsupportedProperties.add(propertyId);
        }
      }
    }
    return unsupportedProperties;
  }


  // ----- AbstractResourceProvider ----------------------------------------

  @Override
  protected Set<String> getPKPropertyIds() {
    return pkPropertyIds;
  }


  // ----- ServiceResourceProvider -----------------------------------------

  RequestStatusResponse installAndStart(String clusterName) throws  SystemException,
      UnsupportedPropertyException, NoSuchParentResourceException {

    final RequestStageContainer requestStages;
    Map<String, Object> installProperties = new HashMap<String, Object>();
    installProperties.put(SERVICE_SERVICE_STATE_PROPERTY_ID, "INSTALLED");
    Map<String, String> requestInfo = new HashMap<String, String>();
    requestInfo.put("context", "Install and start all services");
    Request installRequest = new RequestImpl(null, Collections.singleton(installProperties), requestInfo, null);
    Predicate statePredicate = new EqualsPredicate<String>(SERVICE_SERVICE_STATE_PROPERTY_ID, "INIT");
    Predicate clusterPredicate = new EqualsPredicate<String>(SERVICE_CLUSTER_NAME_PROPERTY_ID, clusterName);
    Predicate installPredicate = new AndPredicate(statePredicate, clusterPredicate);

    final Request startRequest;
    Predicate startPredicate;
    try {
      LOG.info("Installing all services");
      requestStages = doUpdateResources(null, installRequest, installPredicate);
      notifyUpdate(Resource.Type.Service, installRequest, installPredicate);

      Map<String, Object> startProperties = new HashMap<String, Object>();
      startProperties.put(SERVICE_SERVICE_STATE_PROPERTY_ID, "STARTED");
      startRequest = new RequestImpl(null, Collections.singleton(startProperties), requestInfo, null);
      Predicate installedStatePredicate = new EqualsPredicate<String>(SERVICE_SERVICE_STATE_PROPERTY_ID, "INSTALLED");
      Predicate serviceClusterPredicate = new EqualsPredicate<String>(SERVICE_CLUSTER_NAME_PROPERTY_ID, clusterName);
      startPredicate = new AndPredicate(installedStatePredicate, serviceClusterPredicate);

      LOG.info("Starting all services");
      doUpdateResources(requestStages, startRequest, startPredicate);
      notifyUpdate(Resource.Type.Service, startRequest, startPredicate);
      requestStages.persist();
      return requestStages.getRequestStatusResponse();

    } catch (NoSuchResourceException e) {
      throw new SystemException("Attempted to modify a non-existing service",  e);
    }
  }


  // ----- utility methods -------------------------------------------------

  private RequestStageContainer doUpdateResources(final RequestStageContainer stages, final Request request, Predicate predicate)
      throws UnsupportedPropertyException, SystemException, NoSuchResourceException, NoSuchParentResourceException {

    final Set<ServiceRequest> requests = new HashSet<ServiceRequest>();
    RequestStageContainer requestStages = null;

    Iterator<Map<String,Object>> iterator = request.getProperties().iterator();
    if (iterator.hasNext()) {
      for (Map<String, Object> propertyMap : getPropertyMaps(iterator.next(), predicate)) {
        requests.add(getRequest(propertyMap));
      }

      final boolean runSmokeTest = "true".equals(getQueryParameterValue(
          QUERY_PARAMETERS_RUN_SMOKE_TEST_ID, predicate));

      final boolean reconfigureClients = !"false".equals(getQueryParameterValue(
          QUERY_PARAMETERS_RECONFIGURE_CLIENT, predicate));

      requestStages = modifyResources(new Command<RequestStageContainer>() {
        @Override
        public RequestStageContainer invoke() throws AmbariException {
          return updateServices(stages, requests, request.getRequestInfoProperties(),
              runSmokeTest, reconfigureClients);
        }
      });
    }
    return requestStages;
  }

  /**
   * Get a service request object from a map of property values.
   *
   * @param properties  the predicate
   *
   * @return the service request object
   */
  private ServiceRequest getRequest(Map<String, Object> properties) {
    ServiceRequest svcRequest = new ServiceRequest(
        (String) properties.get(SERVICE_CLUSTER_NAME_PROPERTY_ID),
        (String) properties.get(SERVICE_SERVICE_NAME_PROPERTY_ID),
        (String) properties.get(SERVICE_SERVICE_STATE_PROPERTY_ID));
   
    Object o = properties.get(SERVICE_MAINTENANCE_STATE_PROPERTY_ID);
    if (null != o)
      svcRequest.setMaintenanceState(o.toString());

    return svcRequest;
  }

  // Create services from the given request.
  protected synchronized void createServices(Set<ServiceRequest> requests)
      throws AmbariException {

    if (requests.isEmpty()) {
      LOG.warn("Received an empty requests set");
      return;
    }

    Clusters       clusters       = getManagementController().getClusters();
    AmbariMetaInfo ambariMetaInfo = getManagementController().getAmbariMetaInfo();

    // do all validation checks
    Map<String, Set<String>> serviceNames = new HashMap<String, Set<String>>();
    Set<String> duplicates = new HashSet<String>();
    for (ServiceRequest request : requests) {
      if (request.getClusterName() == null
          || request.getClusterName().isEmpty()
          || request.getServiceName() == null
          || request.getServiceName().isEmpty()) {
        throw new IllegalArgumentException("Cluster name and service name"
            + " should be provided when creating a service");
      }

      if (LOG.isDebugEnabled()) {
        LOG.debug("Received a createService request"
            + ", clusterName=" + request.getClusterName()
            + ", serviceName=" + request.getServiceName()
            + ", request=" + request);
      }

      if (!serviceNames.containsKey(request.getClusterName())) {
        serviceNames.put(request.getClusterName(), new HashSet<String>());
      }
      if (serviceNames.get(request.getClusterName())
          .contains(request.getServiceName())) {
        // throw error later for dup
        duplicates.add(request.getServiceName());
        continue;
      }
      serviceNames.get(request.getClusterName()).add(request.getServiceName());

      if (request.getDesiredState() != null
          && !request.getDesiredState().isEmpty()) {
        State state = State.valueOf(request.getDesiredState());
        if (!state.isValidDesiredState()
            || state != State.INIT) {
          throw new IllegalArgumentException("Invalid desired state"
              + " only INIT state allowed during creation"
              + ", providedDesiredState=" + request.getDesiredState());
        }
      }

      Cluster cluster;
      try {
        cluster = clusters.getCluster(request.getClusterName());
      } catch (ClusterNotFoundException e) {
        throw new ParentObjectNotFoundException("Attempted to add a service to a cluster which doesn't exist", e);
      }
      try {
        Service s = cluster.getService(request.getServiceName());
        if (s != null) {
          // throw error later for dup
          duplicates.add(request.getServiceName());
          continue;
        }
      } catch (ServiceNotFoundException e) {
        // Expected
      }

      StackId stackId = cluster.getDesiredStackVersion();
      if (!ambariMetaInfo.isValidService(stackId.getStackName(),
          stackId.getStackVersion(), request.getServiceName())) {
        throw new IllegalArgumentException("Unsupported or invalid service"
            + " in stack"
            + ", clusterName=" + request.getClusterName()
            + ", serviceName=" + request.getServiceName()
            + ", stackInfo=" + stackId.getStackId());
      }
    }

    // ensure only a single cluster update
    if (serviceNames.size() != 1) {
      throw new IllegalArgumentException("Invalid arguments, updates allowed"
          + "on only one cluster at a time");
    }

    // Validate dups
    if (!duplicates.isEmpty()) {
      StringBuilder svcNames = new StringBuilder();
      boolean first = true;
      for (String svcName : duplicates) {
        if (!first) {
          svcNames.append(",");
        }
        first = false;
        svcNames.append(svcName);
      }
      String clusterName = requests.iterator().next().getClusterName();
      String msg;
      if (duplicates.size() == 1) {
        msg = "Attempted to create a service which already exists: "
            + ", clusterName=" + clusterName  + " serviceName=" + svcNames.toString();
      } else {
        msg = "Attempted to create services which already exist: "
            + ", clusterName=" + clusterName  + " serviceNames=" + svcNames.toString();
      }
      throw new DuplicateResourceException(msg);
    }

    ServiceFactory serviceFactory = getManagementController().getServiceFactory();

    // now to the real work
    for (ServiceRequest request : requests) {
      Cluster cluster = clusters.getCluster(request.getClusterName());

      State state = State.INIT;

      // Already checked that service does not exist
      Service s = serviceFactory.createNew(cluster, request.getServiceName());

      s.setDesiredState(state);
      s.setDesiredStackVersion(cluster.getDesiredStackVersion());
      cluster.addService(s);
      s.persist();
    }
  }

  // Get services from the given set of requests.
  protected Set<ServiceResponse> getServices(Set<ServiceRequest> requests)
      throws AmbariException {
    Set<ServiceResponse> response = new HashSet<ServiceResponse>();
    for (ServiceRequest request : requests) {
      try {
        response.addAll(getServices(request));
      } catch (ServiceNotFoundException e) {
        if (requests.size() == 1) {
          // only throw exception if 1 request.
          // there will be > 1 request in case of OR predicate
          throw e;
        }
      }
    }
    return response;
  }

  // Get services from the given request.
  private synchronized Set<ServiceResponse> getServices(ServiceRequest request)
      throws AmbariException {
    if (request.getClusterName() == null
        || request.getClusterName().isEmpty()) {
      throw new AmbariException("Invalid arguments, cluster name"
          + " cannot be null");
    }
    Clusters clusters    = getManagementController().getClusters();
    String   clusterName = request.getClusterName();

    final Cluster cluster;
    try {
      cluster = clusters.getCluster(clusterName);
    } catch (ObjectNotFoundException e) {
      throw new ParentObjectNotFoundException("Parent Cluster resource doesn't exist", e);
    }

    Set<ServiceResponse> response = new HashSet<ServiceResponse>();
    if (request.getServiceName() != null) {
      Service s = cluster.getService(request.getServiceName());
      response.add(s.convertToResponse());
      return response;
    }

    // TODO support search on predicates?

    boolean checkDesiredState = false;
    State desiredStateToCheck = null;
    if (request.getDesiredState() != null
        && !request.getDesiredState().isEmpty()) {
      desiredStateToCheck = State.valueOf(request.getDesiredState());
      if (!desiredStateToCheck.isValidDesiredState()) {
        throw new IllegalArgumentException("Invalid arguments, invalid desired"
            + " state, desiredState=" + desiredStateToCheck);
      }
      checkDesiredState = true;
    }

    for (Service s : cluster.getServices().values()) {
      if (checkDesiredState
          && (desiredStateToCheck != s.getDesiredState())) {
        // skip non matching state
        continue;
      }
      response.add(s.convertToResponse());
    }
    return response;
  }

  // Update services based on the given requests.
  protected synchronized RequestStageContainer updateServices(RequestStageContainer requestStages, Set<ServiceRequest> requests,
                                                      Map<String, String> requestProperties, boolean runSmokeTest,
                                                      boolean reconfigureClients) throws AmbariException {

    AmbariManagementController controller = getManagementController();

    if (requests.isEmpty()) {
      LOG.warn("Received an empty requests set");
      return null;
    }

    Map<State, List<Service>> changedServices
        = new HashMap<State, List<Service>>();
    Map<State, List<ServiceComponent>> changedComps =
        new HashMap<State, List<ServiceComponent>>();
    Map<String, Map<State, List<ServiceComponentHost>>> changedScHosts =
        new HashMap<String, Map<State, List<ServiceComponentHost>>>();
    Collection<ServiceComponentHost> ignoredScHosts =
        new ArrayList<ServiceComponentHost>();

    Set<String> clusterNames = new HashSet<String>();
    Map<String, Set<String>> serviceNames = new HashMap<String, Set<String>>();
    Set<State> seenNewStates = new HashSet<State>();


    Clusters       clusters        = controller.getClusters();
    AmbariMetaInfo ambariMetaInfo   = controller.getAmbariMetaInfo();
    Set<String> maintenanceClusters = new HashSet<String>();

    for (ServiceRequest request : requests) {
      if (request.getClusterName() == null
          || request.getClusterName().isEmpty()
          || request.getServiceName() == null
          || request.getServiceName().isEmpty()) {
        throw new IllegalArgumentException("Invalid arguments, cluster name"
            + " and service name should be provided to update services");
      }

      LOG.info("Received a updateService request"
          + ", clusterName=" + request.getClusterName()
          + ", serviceName=" + request.getServiceName()
          + ", request=" + request.toString());

      clusterNames.add(request.getClusterName());

      if (clusterNames.size() > 1) {
        throw new IllegalArgumentException("Updates to multiple clusters is not"
            + " supported");
      }

      if (!serviceNames.containsKey(request.getClusterName())) {
        serviceNames.put(request.getClusterName(), new HashSet<String>());
      }
      if (serviceNames.get(request.getClusterName())
          .contains(request.getServiceName())) {
        // TODO throw single exception
        throw new IllegalArgumentException("Invalid request contains duplicate"
            + " service names");
      }
      serviceNames.get(request.getClusterName()).add(request.getServiceName());

      Cluster cluster = clusters.getCluster(request.getClusterName());
      Service s = cluster.getService(request.getServiceName());
      State oldState = s.getDesiredState();
      State newState = null;
      if (request.getDesiredState() != null) {
        newState = State.valueOf(request.getDesiredState());
        if (!newState.isValidDesiredState()) {
          throw new IllegalArgumentException("Invalid arguments, invalid"
              + " desired state, desiredState=" + newState);
        }
      }
     
      if (null != request.getMaintenanceState()) {
        MaintenanceState newMaint = MaintenanceState.valueOf(request.getMaintenanceState());
        if (newMaint  != s.getMaintenanceState()) {
          if (newMaint.equals(MaintenanceState.IMPLIED_FROM_HOST)
              || newMaint.equals(MaintenanceState.IMPLIED_FROM_SERVICE)) {
            throw new IllegalArgumentException("Invalid arguments, can only set " +
              "maintenance state to one of " + EnumSet.of(MaintenanceState.OFF, MaintenanceState.ON));
          } else {
            s.setMaintenanceState(newMaint);
           
            maintenanceClusters.add(cluster.getClusterName());
          }
        }
      }

      if (newState == null) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("Nothing to do for new updateService request"
              + ", clusterName=" + request.getClusterName()
              + ", serviceName=" + request.getServiceName()
              + ", newDesiredState=null");
        }
        continue;
      }
     
      if (requests.size() > 1 && MaintenanceState.OFF != s.getMaintenanceState()) {
        LOG.info("Operations cannot be applied to service " + s.getName() +
            " in the maintenance state of " + s.getMaintenanceState());
        continue;
      }
     

      seenNewStates.add(newState);

      if (newState != oldState) {
        if (!State.isValidDesiredStateTransition(oldState, newState)) {
          throw new AmbariException("Invalid transition for"
              + " service"
              + ", clusterName=" + cluster.getClusterName()
              + ", clusterId=" + cluster.getClusterId()
              + ", serviceName=" + s.getName()
              + ", currentDesiredState=" + oldState
              + ", newDesiredState=" + newState);

        }
        if (!changedServices.containsKey(newState)) {
          changedServices.put(newState, new ArrayList<Service>());
        }
        changedServices.get(newState).add(s);
      }

      // TODO should we check whether all servicecomponents and
      // servicecomponenthosts are in the required desired state?

      for (ServiceComponent sc : s.getServiceComponents().values()) {
        State oldScState = sc.getDesiredState();
        if (newState != oldScState) {
          if (sc.isClientComponent() &&
              !newState.isValidClientComponentState()) {
            continue;
          }
          if (!State.isValidDesiredStateTransition(oldScState, newState)) {
            throw new AmbariException("Invalid transition for"
                + " servicecomponent"
                + ", clusterName=" + cluster.getClusterName()
                + ", clusterId=" + cluster.getClusterId()
                + ", serviceName=" + sc.getServiceName()
                + ", componentName=" + sc.getName()
                + ", currentDesiredState=" + oldScState
                + ", newDesiredState=" + newState);
          }
          if (!changedComps.containsKey(newState)) {
            changedComps.put(newState, new ArrayList<ServiceComponent>());
          }
          changedComps.get(newState).add(sc);
        }
        if (LOG.isDebugEnabled()) {
          LOG.debug("Handling update to ServiceComponent"
              + ", clusterName=" + request.getClusterName()
              + ", serviceName=" + s.getName()
              + ", componentName=" + sc.getName()
              + ", currentDesiredState=" + oldScState
              + ", newDesiredState=" + newState);
        }

        for (ServiceComponentHost sch : sc.getServiceComponentHosts().values()) {
          State oldSchState = sch.getState();
          if (oldSchState == State.DISABLED || oldSchState == State.UNKNOWN) {
            //Ignore host components updates in this state
            if (LOG.isDebugEnabled()) {
              LOG.debug("Ignoring ServiceComponentHost"
                  + ", clusterName=" + request.getClusterName()
                  + ", serviceName=" + s.getName()
                  + ", componentName=" + sc.getName()
                  + ", hostname=" + sch.getHostName()
                  + ", currentState=" + oldSchState
                  + ", newDesiredState=" + newState);
            }
            continue;
          }
         
          if (newState == oldSchState) {
            ignoredScHosts.add(sch);
            if (LOG.isDebugEnabled()) {
              LOG.debug("Ignoring ServiceComponentHost"
                  + ", clusterName=" + request.getClusterName()
                  + ", serviceName=" + s.getName()
                  + ", componentName=" + sc.getName()
                  + ", hostname=" + sch.getHostName()
                  + ", currentState=" + oldSchState
                  + ", newDesiredState=" + newState);
            }
            continue;
          }
         
          MaintenanceState schMaint = controller.getEffectiveMaintenanceState(sch);
          if (MaintenanceState.ON == schMaint ||
              (requests.size() > 1 && MaintenanceState.OFF != schMaint)) {
            ignoredScHosts.add(sch);
            if (LOG.isDebugEnabled()) {
              LOG.debug("Ignoring " + schMaint + " ServiceComponentHost"
                  + ", clusterName=" + request.getClusterName()
                  + ", serviceName=" + s.getName()
                  + ", componentName=" + sc.getName()
                  + ", hostname=" + sch.getHostName());
            }
            continue;
          }
          Host host = clusters.getHost(sch.getHostName());

          if (schMaint == MaintenanceState.IMPLIED_FROM_HOST
             && host != null
             && host.getMaintenanceState(cluster.getClusterId()) != MaintenanceState.OFF) {

            // Host is in Passive mode, ignore the SCH
            ignoredScHosts.add(sch);
            LOG.info("Ignoring ServiceComponentHost since "
              + "the host is in passive mode"
              + ", clusterName=" + request.getClusterName()
              + ", serviceName=" + s.getName()
              + ", componentName=" + sc.getName()
              + ", hostname=" + sch.getHostName());
            continue;
          }
         
         
          if (sc.isClientComponent() &&
              !newState.isValidClientComponentState()) {
            continue;
          }
          /**
           * This is hack for now wherein we don't fail if the
           * sch is in INSTALL_FAILED
           */
          if (! isValidStateTransition(requestStages, oldSchState, newState, sch)) {
            String error = "Invalid transition for"
                + " servicecomponenthost"
                + ", clusterName=" + cluster.getClusterName()
                + ", clusterId=" + cluster.getClusterId()
                + ", serviceName=" + sch.getServiceName()
                + ", componentName=" + sch.getServiceComponentName()
                + ", hostname=" + sch.getHostName()
                + ", currentState=" + oldSchState
                + ", newDesiredState=" + newState;
            StackId sid = cluster.getDesiredStackVersion();

            if ( ambariMetaInfo.getComponentCategory(
                sid.getStackName(), sid.getStackVersion(), sc.getServiceName(),
                sch.getServiceComponentName()).isMaster()) {
              throw new AmbariException(error);
            } else {
              LOG.warn("Ignoring: " + error);
              continue;
            }
          }
          if (!changedScHosts.containsKey(sc.getName())) {
            changedScHosts.put(sc.getName(),
                new HashMap<State, List<ServiceComponentHost>>());
          }
          if (!changedScHosts.get(sc.getName()).containsKey(newState)) {
            changedScHosts.get(sc.getName()).put(newState,
                new ArrayList<ServiceComponentHost>());
          }
          if (LOG.isDebugEnabled()) {
            LOG.debug("Handling update to ServiceComponentHost"
                + ", clusterName=" + request.getClusterName()
                + ", serviceName=" + s.getName()
                + ", componentName=" + sc.getName()
                + ", hostname=" + sch.getHostName()
                + ", currentState=" + oldSchState
                + ", newDesiredState=" + newState);
          }
          changedScHosts.get(sc.getName()).get(newState).add(sch);
        }
      }
    }

    if (seenNewStates.size() > 1) {
      // TODO should we handle this scenario
      throw new IllegalArgumentException("Cannot handle different desired state"
          + " changes for a set of services at the same time");
    }
   
    if (maintenanceClusters.size() > 0) {
      try {
        maintenanceStateHelper.createRequests(controller, requestProperties, maintenanceClusters);
      } catch (Exception e) {
        LOG.warn("Could not send maintenance state to Nagios (" + e.getMessage() + ")");
      }
    }

    Cluster cluster = clusters.getCluster(clusterNames.iterator().next());

    return controller.addStages(requestStages, cluster, requestProperties,
      null, changedServices, changedComps, changedScHosts,
        ignoredScHosts, runSmokeTest, reconfigureClients);
  }

  // Delete services based on the given set of requests
  protected RequestStatusResponse deleteServices(Set<ServiceRequest> request)
      throws AmbariException {

    Clusters clusters    = getManagementController().getClusters();
   
    Set<Service> removable = new HashSet<Service>();
   
    for (ServiceRequest serviceRequest : request) {
      if (StringUtils.isEmpty(serviceRequest.getClusterName()) || StringUtils.isEmpty(serviceRequest.getServiceName())) {
        // FIXME throw correct error
        throw new AmbariException("invalid arguments");
      } else {
       
        Service service = clusters.getCluster(
            serviceRequest.getClusterName()).getService(
                serviceRequest.getServiceName());
       
        if (!service.getDesiredState().isRemovableState()) {
          throw new AmbariException("Cannot remove " + service.getName() + ". Desired state " +
              service.getDesiredState() + " is not removable.  Service must be stopped or disabled.");
        } else {
          for (ServiceComponent sc : service.getServiceComponents().values()) {
            if (!sc.canBeRemoved()) {
              throw new AmbariException ("Cannot remove " +
                  serviceRequest.getClusterName() + "/" + serviceRequest.getServiceName() +
                  ". " + sc.getName() + " is in a non-removable state.");
            }
          }
        }
       
        removable.add(service);
      }
    }
   
    for (Service service : removable)
      service.getCluster().deleteService(service.getName());
   
    return null;
  }

  // Get the State of a host component
  private static State getHostComponentState(ServiceComponentHostResponse hostComponent) {
    return State.valueOf(hostComponent.getLiveState());
  }

  // calculate the service state, accounting for the state of the host components
  private String calculateServiceState(String clusterName, String serviceName) {

    ServiceState serviceState = serviceStateMap.get(serviceName);
    if (serviceState == null) {
      serviceState = DEFAULT_SERVICE_STATE;
    }
    State state = serviceState.getState(getManagementController(), clusterName, serviceName);

    return state.toString();
  }


  // ----- inner class ServiceState ------------------------------------------

  /**
   * Interface to allow for different state calculations for different services.
   * TODO : see if this functionality can be moved to service definitions.
   */
  protected interface ServiceState {
    public State getState(AmbariManagementController controller, String clusterName, String serviceName);
  }

  /**
   * Default calculator of service state.
   * The following rules should apply :
   * For services that have all components DISABLED, the service state should be DISABLED.
   * For services that have any master components, the service state should
   * be STARTED if all master components are STARTED.
   * For services that have all client components, the service state should
   * be INSTALLED if all of the components are INSTALLED.
   * For all other cases the state of the service should match the highest state of all
   * of its component states or UNKNOWN if the component states can not be determined.
   */
  protected static class DefaultServiceState implements ServiceState {

    @Override
    public State getState(AmbariManagementController controller, String clusterName, String serviceName) {
      AmbariMetaInfo ambariMetaInfo = controller.getAmbariMetaInfo();
      Clusters       clusters       = controller.getClusters();

      if (clusterName != null && clusterName.length() > 0) {
        try {
          Cluster cluster = clusters.getCluster(clusterName);
          if (cluster != null) {
            StackId stackId = cluster.getDesiredStackVersion();

            ServiceComponentHostRequest request = new ServiceComponentHostRequest(clusterName,
                serviceName, null, null, null);

            Set<ServiceComponentHostResponse> hostComponentResponses =
                controller.getHostComponents(Collections.singleton(request));

            State   masterState = null;
            State   clientState = null;
            State   otherState = null;

            boolean hasDisabled  = false;
            boolean hasMaster    = false;
            boolean hasOther     = false;
            boolean hasClient    = false;

            for (ServiceComponentHostResponse hostComponentResponse : hostComponentResponses ) {
              ComponentInfo componentInfo = ambariMetaInfo.getComponentCategory(stackId.getStackName(),
                  stackId.getStackVersion(), hostComponentResponse.getServiceName(),
                  hostComponentResponse.getComponentName());

              if (componentInfo != null) {
                State state = getHostComponentState(hostComponentResponse);

                if (state.equals(State.DISABLED)) {
                  hasDisabled = true;
                } else {
                  if (componentInfo.isMaster()) {
                    hasMaster = true;
                    if(!state.equals(State.STARTED) &&
                        ( masterState == null || state.ordinal() > masterState.ordinal())) {
                      masterState = state;
                    }
                  } else if (componentInfo.isClient()) {
                    hasClient = true;
                    if (!state.equals(State.INSTALLED) &&
                        (clientState == null || state.ordinal() > clientState.ordinal())) {
                      clientState = state;
                    }
                  } else {
                    hasOther  = true;
                    if (otherState == null || state.ordinal() > otherState.ordinal()) {
                      otherState = state;
                    }
                  }
                }
              }
            }

            return hasMaster   ? masterState == null ? State.STARTED : masterState :
                   hasOther    ? otherState :
                   hasClient   ? clientState == null ? State.INSTALLED : clientState :
                   hasDisabled ? State.DISABLED : State.UNKNOWN;
          }
        } catch (AmbariException e) {
          LOG.error("Can't determine service state.", e);
        }
      }
      return State.UNKNOWN;
    }
  }

  /**
   * Calculator of HDFS service state.
   */
  protected static class HDFSServiceState implements ServiceState {

    @Override
    public State getState(AmbariManagementController controller,String clusterName, String serviceName) {
      AmbariMetaInfo ambariMetaInfo = controller.getAmbariMetaInfo();
      Clusters       clusters       = controller.getClusters();

      if (clusterName != null && clusterName.length() > 0) {
        try {
          Cluster cluster = clusters.getCluster(clusterName);
          if (cluster != null) {
            StackId stackId = cluster.getDesiredStackVersion();

            ServiceComponentHostRequest request = new ServiceComponentHostRequest(clusterName,
                serviceName, null, null, null);

            Set<ServiceComponentHostResponse> hostComponentResponses =
                controller.getHostComponents(Collections.singleton(request));

            int     nameNodeCount       = 0;
            int     nameNodeActiveCount = 0;
            boolean hasSecondary        = false;
            boolean hasJournal          = false;
            State   nonStartedState     = null;

            for (ServiceComponentHostResponse hostComponentResponse : hostComponentResponses ) {
              ComponentInfo componentInfo = ambariMetaInfo.getComponentCategory(stackId.getStackName(),
                  stackId.getStackVersion(), hostComponentResponse.getServiceName(),
                  hostComponentResponse.getComponentName());

              if (componentInfo != null) {
                if (componentInfo.isMaster()) {

                  String componentName = hostComponentResponse.getComponentName();
                  boolean isNameNode = false;

                  if (componentName.equals("NAMENODE")) {
                    ++nameNodeCount;
                    isNameNode = true;
                  } else if (componentName.equals("SECONDARY_NAMENODE")) {
                    hasSecondary = true;
                  } else if (componentName.equals("JOURNALNODE")) {
                    hasJournal = true;
                  }

                  State state = getHostComponentState(hostComponentResponse);

                  switch (state) {
                    case STARTED:
                    case DISABLED:
                      if (isNameNode) {
                        ++nameNodeActiveCount;
                      }
                      break;
                    default:
                      nonStartedState = state;
                  }
                }
              }
            }

            if ( nonStartedState == null ||  // all started
                ((nameNodeCount > 0 && !hasSecondary || hasJournal) &&
                    nameNodeActiveCount > 0)) {  // at least one active namenode
              return State.STARTED;
            }
            return nonStartedState;
          }
        } catch (AmbariException e) {
          LOG.error("Can't determine service state.", e);
        }
      }
      return State.UNKNOWN;
    }
  }

  /**
   * Calculator of HBase service state.
   */
  protected static class HBaseServiceState implements ServiceState {

    @Override
    public State getState(AmbariManagementController controller,String clusterName, String serviceName) {
      AmbariMetaInfo ambariMetaInfo = controller.getAmbariMetaInfo();
      Clusters       clusters       = controller.getClusters();

      if (clusterName != null && clusterName.length() > 0) {
        try {
          Cluster cluster = clusters.getCluster(clusterName);
          if (cluster != null) {
            StackId stackId = cluster.getDesiredStackVersion();

            ServiceComponentHostRequest request = new ServiceComponentHostRequest(clusterName,
                serviceName, null, null, null);

            Set<ServiceComponentHostResponse> hostComponentResponses =
                controller.getHostComponents(Collections.singleton(request));

            int     hBaseMasterActiveCount = 0;
            State   nonStartedState        = null;

            for (ServiceComponentHostResponse hostComponentResponse : hostComponentResponses ) {
              ComponentInfo componentInfo = ambariMetaInfo.getComponentCategory(stackId.getStackName(),
                  stackId.getStackVersion(), hostComponentResponse.getServiceName(),
                  hostComponentResponse.getComponentName());

              if (componentInfo != null) {
                if (componentInfo.isMaster()) {

                  State state = getHostComponentState(hostComponentResponse);

                  switch (state) {
                    case STARTED:
                    case DISABLED:
                      String componentName = hostComponentResponse.getComponentName();
                      if (componentName.equals("HBASE_MASTER")) {
                        ++hBaseMasterActiveCount;
                      }
                      break;
                    default:
                      nonStartedState = state;
                  }
                }
              }
            }

            // should have state INSTALLED when there is no active HBASE_MASTER
            if (hBaseMasterActiveCount > 0) {
              return State.STARTED;
            }
            return nonStartedState == null ? State.INSTALLED : nonStartedState;
          }
        } catch (AmbariException e) {
          LOG.error("Can't determine service state.", e);
        }
      }
      return State.UNKNOWN;
    }
  }

  /**
   * Determine whether a service state change is valid.
   * Looks at projected state from the current stages associated with the request.
   *
   *
   * @param stages        request stages
   * @param startState    service start state
   * @param desiredState  service desired state
   * @param host          host where state change is occurring
   *
   * @return whether the state transition is valid
   */
  private boolean isValidStateTransition(RequestStageContainer stages, State startState,
                                         State desiredState, ServiceComponentHost host) {

    if (stages != null) {
      State projectedState = stages.getProjectedState(host.getHostName(), host.getServiceComponentName());
      startState = projectedState == null ? startState : projectedState;
    }

    return State.isValidStateTransition(startState, desiredState);
  }
}
TOP

Related Classes of org.apache.ambari.server.controller.internal.ServiceResourceProvider$HDFSServiceState

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.