/** * 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.utilities; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.orm.dao.ClusterDAO; import org.apache.ambari.server.orm.dao.MetainfoDAO; import org.apache.ambari.server.orm.entities.ClusterConfigEntity; import org.apache.ambari.server.orm.entities.ClusterEntity; import org.apache.ambari.server.orm.entities.ClusterServiceEntity; import org.apache.ambari.server.orm.entities.ClusterStateEntity; import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity; import org.apache.ambari.server.orm.entities.HostComponentStateEntity; import org.apache.ambari.server.orm.entities.MetainfoEntity; import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity; import org.apache.ambari.server.orm.entities.ServiceDesiredStateEntity; import org.apache.ambari.server.orm.entities.StackEntity; import org.apache.ambari.server.state.ComponentInfo; import org.apache.ambari.server.state.ServiceInfo; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.StackInfo; import org.apache.ambari.server.state.State; import org.apache.ambari.server.utils.VersionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Injector; /*This class should not be used anymore * now we will use DatabaseConsistencyChecker*/ public class DatabaseChecker { static Logger LOG = LoggerFactory.getLogger(DatabaseChecker.class); @Inject static Injector injector; static AmbariMetaInfo ambariMetaInfo; static MetainfoDAO metainfoDAO; public static void checkDBConsistency() throws AmbariException { LOG.info("Checking DB consistency"); boolean checkPassed = true; if (ambariMetaInfo == null) { ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class); } ClusterDAO clusterDAO = injector.getInstance(ClusterDAO.class); List<ClusterEntity> clusters = clusterDAO.findAll(); for (ClusterEntity clusterEntity: clusters) { StackId stackId = new StackId(clusterEntity.getDesiredStack()); Collection<ClusterServiceEntity> serviceEntities = clusterEntity.getClusterServiceEntities(); for (ClusterServiceEntity clusterServiceEntity : serviceEntities) { ServiceDesiredStateEntity serviceDesiredStateEntity = clusterServiceEntity.getServiceDesiredStateEntity(); if (serviceDesiredStateEntity == null) { checkPassed = false; LOG.error(String.format("ServiceDesiredStateEntity is null for " + "ServiceComponentDesiredStateEntity, clusterName=%s, serviceName=%s ", clusterEntity.getClusterName(), clusterServiceEntity.getServiceName())); } Collection<ServiceComponentDesiredStateEntity> scDesiredStateEntities = clusterServiceEntity.getServiceComponentDesiredStateEntities(); if (scDesiredStateEntities == null || scDesiredStateEntities.isEmpty()) { checkPassed = false; LOG.error(String.format("serviceComponentDesiredStateEntities is null or empty for " + "ServiceComponentDesiredStateEntity, clusterName=%s, serviceName=%s ", clusterEntity.getClusterName(), clusterServiceEntity.getServiceName())); } else { for (ServiceComponentDesiredStateEntity scDesiredStateEnity : scDesiredStateEntities) { Collection<HostComponentDesiredStateEntity> schDesiredStateEntities = scDesiredStateEnity.getHostComponentDesiredStateEntities(); Collection<HostComponentStateEntity> schStateEntities = scDesiredStateEnity.getHostComponentStateEntities(); ComponentInfo componentInfo = ambariMetaInfo.getComponent( stackId.getStackName(), stackId.getStackVersion(), scDesiredStateEnity.getServiceName(), scDesiredStateEnity.getComponentName()); boolean zeroCardinality = componentInfo.getCardinality() == null || componentInfo.getCardinality().startsWith("0") || scDesiredStateEnity.getComponentName().equals("SECONDARY_NAMENODE"); // cardinality 0 for NameNode HA boolean componentCheckFailed = false; if (schDesiredStateEntities == null) { componentCheckFailed = true; LOG.error(String.format("hostComponentDesiredStateEntities is null for " + "ServiceComponentDesiredStateEntity, clusterName=%s, serviceName=%s, componentName=%s ", clusterEntity.getClusterName(), scDesiredStateEnity.getServiceName(), scDesiredStateEnity.getComponentName())); } else if (!zeroCardinality && schDesiredStateEntities.isEmpty()) { componentCheckFailed = true; LOG.error(String.format("hostComponentDesiredStateEntities is empty for " + "ServiceComponentDesiredStateEntity, clusterName=%s, serviceName=%s, componentName=%s ", clusterEntity.getClusterName(), scDesiredStateEnity.getServiceName(), scDesiredStateEnity.getComponentName())); } if (schStateEntities == null) { componentCheckFailed = true; LOG.error(String.format("hostComponentStateEntities is null for " + "ServiceComponentDesiredStateEntity, clusterName=%s, serviceName=%s, componentName=%s ", clusterEntity.getClusterName(), scDesiredStateEnity.getServiceName(), scDesiredStateEnity.getComponentName())); } else if (!zeroCardinality && schStateEntities.isEmpty()) { componentCheckFailed = true; LOG.error(String.format("hostComponentStateEntities is empty for " + "ServiceComponentDesiredStateEntity, clusterName=%s, serviceName=%s, componentName=%s ", clusterEntity.getClusterName(), scDesiredStateEnity.getServiceName(), scDesiredStateEnity.getComponentName())); } if (!componentCheckFailed && schDesiredStateEntities.size() != schStateEntities.size()) { checkPassed = false; LOG.error(String.format("HostComponentStateEntities and HostComponentDesiredStateEntities " + "tables must contain equal number of rows mapped to ServiceComponentDesiredStateEntity, " + "(clusterName=%s, serviceName=%s, componentName=%s) ", clusterEntity.getClusterName(), scDesiredStateEnity.getServiceName(), scDesiredStateEnity.getComponentName())); } checkPassed = checkPassed && !componentCheckFailed; } } } } if (checkPassed) { LOG.info("DB consistency check passed."); } else { String errorMessage = "DB consistency check failed. Run \"ambari-server start --skip-database-validation\" to skip validation."; LOG.error(errorMessage); throw new AmbariException(errorMessage); } } private static boolean clusterConfigsContainTypeAndTag(Collection<ClusterConfigEntity> clusterConfigEntities, String typeName, String tag) { for (ClusterConfigEntity clusterConfigEntity : clusterConfigEntities) { if (typeName.equals(clusterConfigEntity.getType()) && tag.equals(clusterConfigEntity.getTag())) { return true; } } return false; } /** * Validates configuration consistency by ensuring that configuration types * only have a single entry in {@link ClusterConfigEntity} which is enabled. * Additionally will check to ensure that every installed service's * configurations are present in the configuration table. * * @throws AmbariException * if check failed * @see ClusterConfigEntity#isSelected() */ public static void checkDBConfigsConsistency() throws AmbariException { LOG.info("Checking DB configs consistency"); boolean checkPassed = true; if (ambariMetaInfo == null) { ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class); } ClusterDAO clusterDAO = injector.getInstance(ClusterDAO.class); List<ClusterEntity> clusters = clusterDAO.findAll(); if (clusters != null) { for (ClusterEntity clusterEntity : clusters) { Collection<ClusterConfigEntity> clusterConfigEntities = clusterEntity.getClusterConfigEntities(); if (null == clusterConfigEntities) { return; } Map<String, Integer> selectedCountForType = new HashMap<>(); for (ClusterConfigEntity configEntity : clusterConfigEntities) { String typeName = configEntity.getType(); if (configEntity.isSelected()) { int selectedCount = selectedCountForType.get(typeName) != null ? selectedCountForType.get(typeName) : 0; selectedCountForType.put(typeName, selectedCount + 1); } else { if (!selectedCountForType.containsKey(typeName)) { selectedCountForType.put(typeName, 0); } } } // Check that every config type from stack is present Collection<ClusterServiceEntity> clusterServiceEntities = clusterEntity.getClusterServiceEntities(); ClusterStateEntity clusterStateEntity = clusterEntity.getClusterStateEntity(); if (clusterStateEntity != null) { StackEntity currentStack = clusterStateEntity.getCurrentStack(); StackInfo stack = ambariMetaInfo.getStack(currentStack.getStackName(), currentStack.getStackVersion()); for (ClusterServiceEntity clusterServiceEntity : clusterServiceEntities) { if (!State.INIT.equals( clusterServiceEntity.getServiceDesiredStateEntity().getDesiredState())) { String serviceName = clusterServiceEntity.getServiceName(); ServiceInfo serviceInfo = ambariMetaInfo.getService(stack.getName(), stack.getVersion(), serviceName); for (String configTypeName : serviceInfo.getConfigTypeAttributes().keySet()) { if (selectedCountForType.get(configTypeName) == null) { checkPassed = false; LOG.error("Configuration {} is missing for service {}", configTypeName, serviceName); } else { // Check that for each config type exactly one is selected if (selectedCountForType.get(configTypeName) == 0) { checkPassed = false; LOG.error("Configuration {} has no enabled entries for service {}", configTypeName, serviceName); } else if (selectedCountForType.get(configTypeName) > 1) { checkPassed = false; LOG.error("Configuration {} has more than 1 enabled entry for service {}", configTypeName, serviceName); } } } } } } } } if (checkPassed) { LOG.info("DB configs consistency check passed."); } else { String errorMessage = "DB configs consistency check failed. Run \"ambari-server start --skip-database-validation\" to skip validation."; LOG.error(errorMessage); throw new AmbariException(errorMessage); } } public static void checkDBVersion() throws AmbariException { LOG.info("Checking DB store version"); if (metainfoDAO == null) { metainfoDAO = injector.getInstance(MetainfoDAO.class); } MetainfoEntity schemaVersionEntity = metainfoDAO.findByKey(Configuration.SERVER_VERSION_KEY); String schemaVersion = null; if (schemaVersionEntity != null) { schemaVersion = schemaVersionEntity.getMetainfoValue(); } Configuration conf = injector.getInstance(Configuration.class); File versionFile = new File(conf.getServerVersionFilePath()); if (!versionFile.exists()) { throw new AmbariException("Server version file does not exist."); } String serverVersion = null; try (Scanner scanner = new Scanner(versionFile)) { serverVersion = scanner.useDelimiter("\\Z").next(); } catch (IOException ioe) { throw new AmbariException("Unable to read server version file."); } if (schemaVersionEntity==null || VersionUtils.compareVersions(schemaVersion, serverVersion, 3) != 0) { String error = "Current database store version is not compatible with " + "current server version" + ", serverVersion=" + serverVersion + ", schemaVersion=" + schemaVersion; LOG.warn(error); throw new AmbariException(error); } LOG.info("DB store version is compatible"); } }