/* * 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.checks; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.Role; import org.apache.ambari.server.RoleCommand; import org.apache.ambari.server.controller.PrereqCheckRequest; import org.apache.ambari.server.controller.internal.PageRequestImpl; import org.apache.ambari.server.controller.internal.RequestImpl; import org.apache.ambari.server.controller.internal.SortRequestImpl; import org.apache.ambari.server.controller.internal.TaskResourceProvider; import org.apache.ambari.server.controller.spi.PageRequest; import org.apache.ambari.server.controller.spi.Predicate; import org.apache.ambari.server.controller.spi.SortRequest; import org.apache.ambari.server.controller.spi.SortRequestProperty; import org.apache.ambari.server.controller.utilities.PredicateBuilder; import org.apache.ambari.server.orm.dao.HostRoleCommandDAO; import org.apache.ambari.server.orm.dao.ServiceConfigDAO; import org.apache.ambari.server.orm.entities.HostRoleCommandEntity; import org.apache.ambari.server.orm.entities.ServiceConfigEntity; import org.apache.ambari.server.state.Cluster; 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.StackId; import org.apache.ambari.server.state.stack.PrereqCheckStatus; import org.apache.ambari.server.state.stack.PrerequisiteCheck; import org.apache.ambari.server.state.stack.upgrade.UpgradeType; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; /** * Checks that all Service Checks are less recent than last * configuration update for given services. * That is a potential problem when doing stack update. */ @Singleton @UpgradeCheck( group = UpgradeCheckGroup.DEFAULT, required = { UpgradeType.ROLLING, UpgradeType.NON_ROLLING, UpgradeType.HOST_ORDERED }) public class ServiceCheckValidityCheck extends AbstractCheckDescriptor { private static final Logger LOG = LoggerFactory.getLogger(ServiceCheckValidityCheck.class); private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss"); private static List<SortRequestProperty> sortRequestProperties = Collections.singletonList(new SortRequestProperty(TaskResourceProvider.TASK_START_TIME_PROPERTY_ID, SortRequest.Order.DESC)); private static SortRequest sortRequest = new SortRequestImpl(sortRequestProperties); private static final PageRequestImpl PAGE_REQUEST = new PageRequestImpl(PageRequest.StartingPoint.End, 1000, 0, null, null); private static final RequestImpl REQUEST = new RequestImpl(null, null, null, null, sortRequest, PAGE_REQUEST); private static final Predicate PREDICATE = new PredicateBuilder().property(TaskResourceProvider.TASK_COMMAND_PROPERTY_ID) .equals(RoleCommand.SERVICE_CHECK.name()).toPredicate(); @Inject Provider<ServiceConfigDAO> serviceConfigDAOProvider; @Inject Provider<HostRoleCommandDAO> hostRoleCommandDAOProvider; /** * Constructor. */ public ServiceCheckValidityCheck() { super(CheckDescription.SERVICE_CHECK); } /** * {@inheritDoc} */ @Override public void perform(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) throws AmbariException { ServiceConfigDAO serviceConfigDAO = serviceConfigDAOProvider.get(); HostRoleCommandDAO hostRoleCommandDAO = hostRoleCommandDAOProvider.get(); final String clusterName = request.getClusterName(); final Cluster cluster = clustersProvider.get().getCluster(clusterName); long clusterId = cluster.getClusterId(); Map<String, Long> lastServiceConfigUpdates = new HashMap<>(); for (Service service : cluster.getServices().values()) { if (service.getMaintenanceState() != MaintenanceState.OFF || !hasAtLeastOneComponentVersionAdvertised(service)) { continue; } StackId stackId = cluster.getCurrentStackVersion(); boolean isServiceWitNoConfigs = ambariMetaInfo.get().isServiceWithNoConfigs(stackId.getStackName(), stackId.getStackVersion(), service.getName()); if (isServiceWitNoConfigs){ LOG.info(String.format("%s in %s version %s does not have customizable configurations. Skip checking service configuration history.", service.getName(), stackId.getStackName(), stackId.getStackVersion())); } else { LOG.info(String.format("%s in %s version %s has customizable configurations. Check service configuration history.", service.getName(), stackId.getStackName(), stackId.getStackVersion())); ServiceConfigEntity lastServiceConfig = serviceConfigDAO.getLastServiceConfig(clusterId, service.getName()); lastServiceConfigUpdates.put(service.getName(), lastServiceConfig.getCreateTimestamp()); } } List<HostRoleCommandEntity> commands = hostRoleCommandDAO.findAll(REQUEST, PREDICATE); // !!! build a map of Role to latest-config-check in case it was rerun multiple times, we want the latest Map<Role, HostRoleCommandEntity> latestTimestamps = new HashMap<>(); for (HostRoleCommandEntity command : commands) { Role role = command.getRole(); // Because results are already sorted by start_time desc, first occurrence is guaranteed to have max(start_time). if (!latestTimestamps.containsKey(role)) { latestTimestamps.put(role, command); } } LinkedHashSet<String> failedServiceNames = new LinkedHashSet<>(); for (Map.Entry<String, Long> serviceEntry : lastServiceConfigUpdates.entrySet()) { String serviceName = serviceEntry.getKey(); Long configTimestamp = serviceEntry.getValue(); boolean serviceCheckWasExecuted = false; for (HostRoleCommandEntity command : latestTimestamps.values()) { if (command.getCommandDetail().contains(serviceName)) { serviceCheckWasExecuted = true; Long serviceCheckTimestamp = command.getStartTime(); if (serviceCheckTimestamp < configTimestamp) { failedServiceNames.add(serviceName); LOG.info("Service {} latest config change is {}, latest service check executed at {}", serviceName, DATE_FORMAT.format(new Date(configTimestamp)), DATE_FORMAT.format(new Date(serviceCheckTimestamp))); } } } if (!serviceCheckWasExecuted) { failedServiceNames.add(serviceName); LOG.info("Service {} service check has never been executed", serviceName); } } if (!failedServiceNames.isEmpty()) { prerequisiteCheck.setFailedOn(failedServiceNames); prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL); String failReason = getFailReason(prerequisiteCheck, request); prerequisiteCheck.setFailReason(String.format(failReason, StringUtils.join(failedServiceNames, ", "))); } } private boolean hasAtLeastOneComponentVersionAdvertised(Service service) { Collection<ServiceComponent> components = service.getServiceComponents().values(); for (ServiceComponent component : components) { if (component.isVersionAdvertised()) { return true; } } return false; } }