Return-Path: X-Original-To: apmail-ambari-commits-archive@www.apache.org Delivered-To: apmail-ambari-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 8400118FEA for ; Thu, 11 Feb 2016 13:59:07 +0000 (UTC) Received: (qmail 20746 invoked by uid 500); 11 Feb 2016 13:58:45 -0000 Delivered-To: apmail-ambari-commits-archive@ambari.apache.org Received: (qmail 20716 invoked by uid 500); 11 Feb 2016 13:58:45 -0000 Mailing-List: contact commits-help@ambari.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ambari-dev@ambari.apache.org Delivered-To: mailing list commits@ambari.apache.org Received: (qmail 20707 invoked by uid 99); 11 Feb 2016 13:58:45 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 11 Feb 2016 13:58:45 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 0EFD8E0534; Thu, 11 Feb 2016 13:58:45 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: vbrodetskyi@apache.org To: commits@ambari.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: ambari git commit: AMBARI-14948. Config consistency checker.(vbrodetskyi) Date: Thu, 11 Feb 2016 13:58:45 +0000 (UTC) Repository: ambari Updated Branches: refs/heads/branch-2.2 7ce5a0072 -> 58b0b5014 AMBARI-14948. Config consistency checker.(vbrodetskyi) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/58b0b501 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/58b0b501 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/58b0b501 Branch: refs/heads/branch-2.2 Commit: 58b0b5014ee3753230782b9e97c52662c25ceed2 Parents: 7ce5a00 Author: Vitaly Brodetskyi Authored: Thu Feb 11 01:10:42 2016 +0200 Committer: Vitaly Brodetskyi Committed: Thu Feb 11 01:10:42 2016 +0200 ---------------------------------------------------------------------- ambari-server/sbin/ambari-server | 6 +- .../server/checks/CheckDatabaseHelper.java | 473 +++++++++++++++++++ ambari-server/src/main/python/ambari-server.py | 9 +- .../main/python/ambari_server/checkDatabase.py | 80 ++++ .../main/python/ambari_server/setupActions.py | 1 + .../server/checks/CheckDatabaseHelperTest.java | 292 ++++++++++++ .../src/test/python/TestAmbariServer.py | 37 ++ 7 files changed, 894 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/58b0b501/ambari-server/sbin/ambari-server ---------------------------------------------------------------------- diff --git a/ambari-server/sbin/ambari-server b/ambari-server/sbin/ambari-server index 72d94ed..e676cbc 100755 --- a/ambari-server/sbin/ambari-server +++ b/ambari-server/sbin/ambari-server @@ -138,9 +138,13 @@ case "$1" in echo -e "Updating host names" $PYTHON /usr/sbin/ambari-server.py $@ ;; + check-database) + echo -e "Checking database" + $PYTHON /usr/sbin/ambari-server.py $@ + ;; *) echo "Usage: /usr/sbin/ambari-server - {start|stop|restart|setup|setup-jce|upgrade|status|upgradestack|setup-ldap|sync-ldap|set-current|setup-security|refresh-stack-hash|backup|restore|update-host-names} [options] + {start|stop|restart|setup|setup-jce|upgrade|status|upgradestack|setup-ldap|sync-ldap|set-current|setup-security|refresh-stack-hash|backup|restore|update-host-names|check-database} [options] Use usr/sbin/ambari-server --help to get details on options available. Or, simply invoke ambari-server.py --help to print the options." exit 1 http://git-wip-us.apache.org/repos/asf/ambari/blob/58b0b501/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDatabaseHelper.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDatabaseHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDatabaseHelper.java new file mode 100644 index 0000000..a078c8a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/checks/CheckDatabaseHelper.java @@ -0,0 +1,473 @@ +/* + * 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 com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.persist.PersistService; +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.controller.ControllerModule; +import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.state.ServiceInfo; +import org.apache.ambari.server.utils.EventBusSynchronizer; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/* +* Class for database validation. +* Here we will check configs, services, components and etc. +*/ +public class CheckDatabaseHelper { + private static final Logger LOG = LoggerFactory.getLogger + (CheckDatabaseHelper.class); + + private static final String AUTHENTICATED_USER_NAME = "ambari-check-database"; + + private PersistService persistService; + private DBAccessor dbAccessor; + private Connection connection; + private AmbariMetaInfo ambariMetaInfo; + private Injector injector; + + @Inject + public CheckDatabaseHelper(DBAccessor dbAccessor, + Injector injector, + PersistService persistService) { + this.dbAccessor = dbAccessor; + this.injector = injector; + this.persistService = persistService; + } + + /** + * Extension of main controller module + */ + public static class CheckHelperModule extends ControllerModule { + + public CheckHelperModule() throws Exception { + } + + @Override + protected void configure() { + super.configure(); + EventBusSynchronizer.synchronizeAmbariEventPublisher(binder()); + } + } + + /* + * init method to create connection + * */ + protected void init() { + connection = dbAccessor.getConnection(); + ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class); + } + + /* + * method to close connection + * */ + private void closeConnection() { + try { + connection.close(); + } catch (SQLException e) { + LOG.error("Exception occurred during connection close procedure: ", e); + } + } + + public void startPersistenceService() { + persistService.start(); + } + + public void stopPersistenceService() { + persistService.stop(); + } + + /* + * This method checks if all configurations that we have in clusterconfig table + * have at least one mapping in clusterconfigmapping table. If we found not mapped config + * then we are showing warning message for user. + * */ + protected void checkForNotMappedConfigsToCluster() { + String GET_NOT_MAPPED_CONFIGS_QUERY = "select type_name from clusterconfig where type_name not in (select type_name from clusterconfigmapping)"; + Set nonSelectedConfigs = new HashSet<>(); + ResultSet rs = null; + try { + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + rs = statement.executeQuery(GET_NOT_MAPPED_CONFIGS_QUERY); + if (rs != null) { + while (rs.next()) { + nonSelectedConfigs.add(rs.getString("type_name")); + } + } + if (!nonSelectedConfigs.isEmpty()) { + LOG.warn("You have config(s) that is(are) not mapped to any cluster: " + StringUtils.join(nonSelectedConfigs, ",")); + } + } catch (SQLException e) { + LOG.error("Exception occurred during check for not mapped configs to cluster procedure: ", e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + LOG.error("Exception occurred during result set closing procedure: ", e); + } + } + } + } + + /* + * This method checks if any config type in clusterconfigmapping table, has + * more than one versions selected. If config version is selected(in selected column = 1), + * it means that this version of config is actual. So, if any config type has more + * than one selected version it's a bug and we are showing error message for user. + * */ + protected void checkForConfigsSelectedMoreThanOnce() { + String GET_CONFIGS_SELECTED_MORE_THAN_ONCE_QUERY = "select type_name from clusterconfigmapping group by type_name having sum(selected) > 1"; + Set configsSelectedMoreThanOnce = new HashSet<>(); + ResultSet rs = null; + try { + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + rs = statement.executeQuery(GET_CONFIGS_SELECTED_MORE_THAN_ONCE_QUERY); + if (rs != null) { + while (rs.next()) { + configsSelectedMoreThanOnce.add(rs.getString("type_name")); + } + } + if (!configsSelectedMoreThanOnce.isEmpty()) { + LOG.error("You have config(s) that is(are) selected more than once in clusterconfigmapping: " + StringUtils.join(configsSelectedMoreThanOnce, ",")); + } + } catch (SQLException e) { + LOG.error("Exception occurred during check for config selected more than ones procedure: ", e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + LOG.error("Exception occurred during result set closing procedure: ", e); + } + } + } + } + + /* + * This method checks if all hosts from hosts table + * has related host state info in hoststate table. + * If not then we are showing error. + * */ + protected void checkForHostsWithoutState() { + String GET_HOSTS_WITHOUT_STATUS_QUERY = "select host_name from hosts where host_id not in (select host_id from hoststate)"; + Set hostsWithoutStatus = new HashSet<>(); + ResultSet rs = null; + try { + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + rs = statement.executeQuery(GET_HOSTS_WITHOUT_STATUS_QUERY); + if (rs != null) { + while (rs.next()) { + LOG.error(rs.getString("host_name")); + LOG.error(rs.getString("HOST_NAME")); + System.out.println("ERROR" + rs.getString("HOST_NAME")); + System.out.println("ERROR" + rs.getString("host_name")); + hostsWithoutStatus.add(rs.getString("host_name")); + } + } + + if (!hostsWithoutStatus.isEmpty()) { + LOG.error("You have host(s) without status: " + StringUtils.join(hostsWithoutStatus, ",")); + } + } catch (SQLException e) { + LOG.error("Exception occurred during check for host without state procedure: ", e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + LOG.error("Exception occurred during result set closing procedure: ", e); + } + } + } + } + + /* + * This method checks if count of host component states equals count + * of desired host component states. According to ambari logic these + * two tables should have the same count of rows. If not then we are + * showing error for user. + * */ + protected void checkHostComponentStatesCountEqualsHostComponentsDesiredStates() { + String GET_HOST_COMPONENT_STATE_COUNT_QUERY = "select count(*) from hostcomponentstate"; + String GET_HOST_COMPONENT_DESIRED_STATE_COUNT_QUERY = "select count(*) from hostcomponentdesiredstate"; + String GET_MERGED_TABLE_ROW_COUNT_QUERY = "select count(*) FROM hostcomponentstate hcs " + + "JOIN hostcomponentdesiredstate hcds ON hcs.service_name = hcds.service_name AND hcs.component_name = hcds.component_name AND hcs.host_id = hcds.host_id"; + int hostComponentStateCount = 0; + int hostComponentDesiredStateCount = 0; + int mergedCount = 0; + ResultSet rs = null; + try { + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + rs = statement.executeQuery(GET_HOST_COMPONENT_STATE_COUNT_QUERY); + if (rs != null) { + while (rs.next()) { + hostComponentStateCount = rs.getInt(1); + } + } + + rs = statement.executeQuery(GET_HOST_COMPONENT_DESIRED_STATE_COUNT_QUERY); + if (rs != null) { + while (rs.next()) { + hostComponentDesiredStateCount = rs.getInt(1); + } + } + + rs = statement.executeQuery(GET_MERGED_TABLE_ROW_COUNT_QUERY); + if (rs != null) { + while (rs.next()) { + mergedCount = rs.getInt(1); + } + } + + if (hostComponentStateCount != hostComponentDesiredStateCount || hostComponentStateCount != mergedCount) { + LOG.error("Your host component state count not equals host component desired state count!"); + } + + } catch (SQLException e) { + LOG.error("Exception occurred during check for same count of host component states and host component desired states: ", e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + LOG.error("Exception occurred during result set closing procedure: ", e); + } + } + } + + } + + + /* + * This method checks several potential problems for services: + * 1) Check if we have services in cluster which doesn't have service config id(not available in serviceconfig table). + * 2) Check if service has no mapped configs to it's service config id. + * 3) Check if service has all required configs mapped to it. + * 4) Check if service has config which is not selected(has no actual config version) in clusterconfigmapping table. + * If any issue was discovered, we are showing error message for user. + * */ + protected void checkServiceConfigs() { + String GET_SERVICES_WITHOUT_CONFIGS_QUERY = "select service_name from clusterservices where service_name not in (select service_name from serviceconfig where group_id is null)"; + String GET_SERVICE_CONFIG_WITHOUT_MAPPING_QUERY = "select service_name from serviceconfig where service_config_id not in (select service_config_id from serviceconfigmapping) and group_id is null"; + String GET_STACK_NAME_VERSION_QUERY = "select s.stack_name, s.stack_version from clusters c join stack s on c.desired_stack_id = s.stack_id"; + String GET_SERVICES_WITH_CONFIGS_QUERY = "select cs.service_name, type_name, sc.version from clusterservices cs " + + "join serviceconfig sc on cs.service_name=sc.service_name " + + "join serviceconfigmapping scm on sc.service_config_id=scm.service_config_id " + + "join clusterconfig cc on scm.config_id=cc.config_id " + + "where sc.group_id is null " + + "group by cs.service_name, type_name, sc.version"; + String GET_NOT_SELECTED_SERVICE_CONFIGS_QUERY = "select cs.service_name,cc.type_name from clusterservices cs " + + "join serviceconfig sc on cs.service_name=sc.service_name " + + "join serviceconfigmapping scm on sc.service_config_id=scm.service_config_id " + + "join clusterconfig cc on scm.config_id=cc.config_id " + + "join clusterconfigmapping ccm on cc.type_name=ccm.type_name and cc.version_tag=ccm.version_tag " + + "where sc.group_id is null and sc.service_config_id = (select max(service_config_id) from serviceconfig sc2 where sc2.service_name=sc.service_name) " + + "group by cs.service_name,cc.type_name " + + "having sum(ccm.selected) < 1"; + String stackName = null, stackVersion = null; + Set servicesWithoutConfigs = new HashSet<>(); + Set servicesWithoutMappedConfigs = new HashSet<>(); + Map> notSelectedServiceConfigs = new HashMap<>(); + ResultSet rs = null; + + try { + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + rs = statement.executeQuery(GET_SERVICES_WITHOUT_CONFIGS_QUERY); + if (rs != null) { + while (rs.next()) { + servicesWithoutConfigs.add(rs.getString("service_name")); + } + } + + if (!servicesWithoutConfigs.isEmpty()) { + LOG.error("You have services without configs at all: " + StringUtils.join(servicesWithoutConfigs, ",")); + } + + rs = statement.executeQuery(GET_SERVICE_CONFIG_WITHOUT_MAPPING_QUERY); + if (rs != null) { + while (rs.next()) { + servicesWithoutMappedConfigs.add(rs.getString("service_name")); + } + } + + if (!servicesWithoutMappedConfigs.isEmpty()) { + LOG.error("You have services without mapped configs: " + StringUtils.join(servicesWithoutMappedConfigs, ",")); + } + + rs = statement.executeQuery(GET_STACK_NAME_VERSION_QUERY); + if (rs != null) { + while (rs.next()) { + stackName = rs.getString("stack_name"); + stackVersion = rs.getString("stack_version"); + } + } + + if (stackName != null && stackVersion != null) { + Set serviceNames = new HashSet<>(); + Map> dbServiceVersionConfigs = new HashMap<>(); + Multimap stackServiceConfigs = HashMultimap.create(); + + rs = statement.executeQuery(GET_SERVICES_WITH_CONFIGS_QUERY); + if (rs != null) { + String serviceName = null, configType = null; + Integer serviceVersion = null; + while (rs.next()) { + serviceName = rs.getString("service_name"); + configType = rs.getString("type_name"); + serviceVersion = rs.getInt("version"); + + serviceNames.add(serviceName); + + if (dbServiceVersionConfigs.get(serviceVersion) == null) { + Multimap dbServiceConfigs = HashMultimap.create(); + dbServiceConfigs.put(serviceName, configType); + dbServiceVersionConfigs.put(serviceVersion, dbServiceConfigs); + } else { + dbServiceVersionConfigs.get(serviceVersion).put(serviceName, configType); + } + } + } + + + Map serviceInfoMap = ambariMetaInfo.getServices(stackName, stackVersion); + for (String serviceName : serviceNames) { + ServiceInfo serviceInfo = serviceInfoMap.get(serviceName); + Set configTypes = serviceInfo.getConfigTypeAttributes().keySet(); + for (String configType : configTypes) { + stackServiceConfigs.put(serviceName, configType); + } + } + + for (Integer serviceVersion : dbServiceVersionConfigs.keySet()) { + Multimap dbServiceConfigs = dbServiceVersionConfigs.get(serviceVersion); + for (String serviceName : dbServiceConfigs.keySet()) { + Collection serviceConfigsFromStack = stackServiceConfigs.get(serviceName); + Collection serviceConfigsFromDB = dbServiceConfigs.get(serviceName); + if (serviceConfigsFromDB != null && serviceConfigsFromStack != null) { + serviceConfigsFromStack.removeAll(serviceConfigsFromDB); + if (!serviceConfigsFromStack.isEmpty()) { + LOG.error(String.format("Required config(s): %s is(are) not available for service %s with service config version %s", + StringUtils.join(serviceConfigsFromStack, ","), serviceName, Integer.toString(serviceVersion))); + } + } + } + } + } + + rs = statement.executeQuery(GET_NOT_SELECTED_SERVICE_CONFIGS_QUERY); + if (rs != null) { + String serviceName = null, configType = null; + while (rs.next()) { + serviceName = rs.getString("service_name"); + configType = rs.getString("type_name"); + + if (notSelectedServiceConfigs.get(serviceName) != null) { + notSelectedServiceConfigs.get(serviceName).add(configType); + } else { + List configTypes = new ArrayList<>(); + configTypes.add(configType); + notSelectedServiceConfigs.put(serviceName, configTypes); + } + } + } + + for (String serviceName : notSelectedServiceConfigs.keySet()) { + LOG.error(String.format("You have non selected configs: %s for service %s.", StringUtils.join(notSelectedServiceConfigs.get(serviceName), ","), serviceName)); + } + } catch (SQLException e) { + LOG.error("Exception occurred during complex service check procedure: ", e); + } catch (AmbariException e) { + LOG.error("Exception occurred during complex service check procedure: ", e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + LOG.error("Exception occurred during result set closing procedure: ", e); + } + } + } + + } + + /* + * Main method from which we are calling all checks + * */ + public static void main(String[] args) throws Exception { + CheckDatabaseHelper checkDatabaseHelper = null; + try { + LOG.info("******************************* Check database started *******************************"); + + Injector injector = Guice.createInjector(new CheckHelperModule()); + checkDatabaseHelper = injector.getInstance(CheckDatabaseHelper.class); + + checkDatabaseHelper.startPersistenceService(); + + checkDatabaseHelper.init(); + + checkDatabaseHelper.checkForNotMappedConfigsToCluster(); + + checkDatabaseHelper.checkForConfigsSelectedMoreThanOnce(); + + checkDatabaseHelper.checkForHostsWithoutState(); + + checkDatabaseHelper.checkHostComponentStatesCountEqualsHostComponentsDesiredStates(); + + checkDatabaseHelper.checkServiceConfigs(); + + checkDatabaseHelper.stopPersistenceService(); + + LOG.info("******************************* Check database completed *******************************"); + } catch (Throwable e) { + if (e instanceof AmbariException) { + LOG.error("Exception occurred during database check:", e); + throw (AmbariException)e; + }else{ + LOG.error("Unexpected error, database check failed", e); + throw new Exception("Unexpected error, database check failed", e); + } + } finally { + if (checkDatabaseHelper != null) { + checkDatabaseHelper.closeConnection(); + } + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/58b0b501/ambari-server/src/main/python/ambari-server.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/python/ambari-server.py b/ambari-server/src/main/python/ambari-server.py index 8be79a5..38bf11f 100755 --- a/ambari-server/src/main/python/ambari-server.py +++ b/ambari-server/src/main/python/ambari-server.py @@ -37,10 +37,12 @@ from ambari_server.serverSetup import reset, setup, setup_jce_policy from ambari_server.serverUpgrade import upgrade, upgrade_stack, set_current from ambari_server.setupHttps import setup_https, setup_truststore from ambari_server.hostUpdate import update_host_names +from ambari_server.checkDatabase import check_database from ambari_server.setupActions import BACKUP_ACTION, LDAP_SETUP_ACTION, LDAP_SYNC_ACTION, PSTART_ACTION, \ - REFRESH_STACK_HASH_ACTION, RESET_ACTION, RESTORE_ACTION, UPDATE_HOST_NAMES_ACTION, SETUP_ACTION, SETUP_SECURITY_ACTION, \ - START_ACTION, STATUS_ACTION, STOP_ACTION, UPGRADE_ACTION, UPGRADE_STACK_ACTION, SETUP_JCE_ACTION, SET_CURRENT_ACTION + REFRESH_STACK_HASH_ACTION, RESET_ACTION, RESTORE_ACTION, UPDATE_HOST_NAMES_ACTION, CHECK_DATABASE_ACTION, \ + SETUP_ACTION, SETUP_SECURITY_ACTION,START_ACTION, STATUS_ACTION, STOP_ACTION, UPGRADE_ACTION, UPGRADE_STACK_ACTION, \ + SETUP_JCE_ACTION, SET_CURRENT_ACTION from ambari_server.setupSecurity import setup_ldap, sync_ldap, setup_master_key, setup_ambari_krb5_jaas from ambari_server.userInput import get_validated_string_input @@ -523,7 +525,8 @@ def create_user_action_map(args, options): REFRESH_STACK_HASH_ACTION: UserAction(refresh_stack_hash_action), BACKUP_ACTION: UserActionPossibleArgs(backup, [1, 2], args), RESTORE_ACTION: UserActionPossibleArgs(restore, [1, 2], args), - UPDATE_HOST_NAMES_ACTION: UserActionPossibleArgs(update_host_names, [2], args, options) + UPDATE_HOST_NAMES_ACTION: UserActionPossibleArgs(update_host_names, [2], args, options), + CHECK_DATABASE_ACTION: UserAction(check_database, options) } return action_map http://git-wip-us.apache.org/repos/asf/ambari/blob/58b0b501/ambari-server/src/main/python/ambari_server/checkDatabase.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/python/ambari_server/checkDatabase.py b/ambari-server/src/main/python/ambari_server/checkDatabase.py new file mode 100644 index 0000000..af1d74f --- /dev/null +++ b/ambari-server/src/main/python/ambari_server/checkDatabase.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +''' +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. +''' + +import os +import sys + +from ambari_commons.exceptions import FatalException +from ambari_server import serverConfiguration +from ambari_server import dbConfiguration +from ambari_server import setupSecurity +from ambari_commons import os_utils +from ambari_server import userInput +from ambari_server import serverUtils +from ambari_server.serverConfiguration import configDefaults, get_java_exe_path, get_ambari_properties, read_ambari_user, \ + parse_properties_file, JDBC_DATABASE_PROPERTY +from ambari_commons.logging_utils import print_info_msg, print_warning_msg, print_error_msg +from ambari_server.dbConfiguration import ensure_jdbc_driver_is_installed, LINUX_DBMS_KEYS_LIST +from ambari_server.serverClassPath import ServerClassPath +from ambari_server.setupSecurity import ensure_can_start_under_current_user, generate_env +from ambari_commons.os_utils import run_os_command +from ambari_server.serverUtils import is_server_runing +from ambari_server.userInput import get_YN_input + +CHECK_DATABASE_HELPER_CMD = "{0} -cp {1} " + \ + "org.apache.ambari.server.checks.CheckDatabaseHelper" + \ + " > " + configDefaults.SERVER_LOG_FILE + " 2>&1" + +def check_database(options): + + jdk_path = serverConfiguration.get_java_exe_path() + + if jdk_path is None: + print_error_msg("No JDK found, please run the \"setup\" " + "command to install a JDK automatically or install any " + "JDK manually to " + configDefaults.JDK_INSTALL_DIR) + sys.exit(1) + + properties = serverConfiguration.get_ambari_properties() + serverConfiguration.parse_properties_file(options) + options.database_index = LINUX_DBMS_KEYS_LIST.index(properties[JDBC_DATABASE_PROPERTY]) + + dbConfiguration.ensure_jdbc_driver_is_installed(options, serverConfiguration.get_ambari_properties()) + + serverClassPath = ServerClassPath(serverConfiguration.get_ambari_properties(), options) + class_path = serverClassPath.get_full_ambari_classpath_escaped_for_shell() + + command = CHECK_DATABASE_HELPER_CMD.format(jdk_path, class_path) + + ambari_user = serverConfiguration.read_ambari_user() + current_user = setupSecurity.ensure_can_start_under_current_user(ambari_user) + environ = setupSecurity.generate_env(options, ambari_user, current_user) + + (retcode, stdout, stderr) = os_utils.run_os_command(command, env=environ) + print_info_msg("Return code from check database command, retcode = " + str(retcode)) + + if retcode > 0: + print_error_msg("Database check failed to complete. Please check ambari-server.log for problem.") + raise FatalException(1, 'Database check failed.') + else: + print_info_msg('Check database completed successfully. Please check ambari-server.log for results.') + + + http://git-wip-us.apache.org/repos/asf/ambari/blob/58b0b501/ambari-server/src/main/python/ambari_server/setupActions.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/python/ambari_server/setupActions.py b/ambari-server/src/main/python/ambari_server/setupActions.py index 686ac9a..324ef69 100644 --- a/ambari-server/src/main/python/ambari_server/setupActions.py +++ b/ambari-server/src/main/python/ambari_server/setupActions.py @@ -36,6 +36,7 @@ SETUP_GANGLIA_HTTPS_ACTION = "setup-ganglia-https" ENCRYPT_PASSWORDS_ACTION = "encrypt-passwords" SETUP_SECURITY_ACTION = "setup-security" UPDATE_HOST_NAMES_ACTION = "update-host-names" +CHECK_DATABASE_ACTION = "check-database" BACKUP_ACTION = "backup" RESTORE_ACTION = "restore" SETUP_JCE_ACTION = "setup-jce" http://git-wip-us.apache.org/repos/asf/ambari/blob/58b0b501/ambari-server/src/test/java/org/apache/ambari/server/checks/CheckDatabaseHelperTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/checks/CheckDatabaseHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/checks/CheckDatabaseHelperTest.java new file mode 100644 index 0000000..e329ab7 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/checks/CheckDatabaseHelperTest.java @@ -0,0 +1,292 @@ +/* + * 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 com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.stack.StackManagerFactory; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.ServiceInfo; +import org.apache.ambari.server.state.stack.OsFamily; +import org.easymock.EasyMockSupport; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; + +import static org.easymock.EasyMock.expect; + +public class CheckDatabaseHelperTest { + + @Test + public void testCheckForNotMappedConfigs() throws Exception { + EasyMockSupport easyMockSupport = new EasyMockSupport(); + final AmbariMetaInfo mockAmbariMetainfo = easyMockSupport.createNiceMock(AmbariMetaInfo.class); + final DBAccessor mockDBDbAccessor = easyMockSupport.createNiceMock(DBAccessor.class); + final Connection mockConnection = easyMockSupport.createNiceMock(Connection.class); + final ResultSet mockResultSet = easyMockSupport.createNiceMock(ResultSet.class); + final Statement mockStatement = easyMockSupport.createNiceMock(Statement.class); + + final StackManagerFactory mockStackManagerFactory = easyMockSupport.createNiceMock(StackManagerFactory.class); + final EntityManager mockEntityManager = easyMockSupport.createNiceMock(EntityManager.class); + final Clusters mockClusters = easyMockSupport.createNiceMock(Clusters.class); + final OsFamily mockOSFamily = easyMockSupport.createNiceMock(OsFamily.class); + final Injector mockInjector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(AmbariMetaInfo.class).toInstance(mockAmbariMetainfo); + bind(StackManagerFactory.class).toInstance(mockStackManagerFactory); + bind(EntityManager.class).toInstance(mockEntityManager); + bind(DBAccessor.class).toInstance(mockDBDbAccessor); + bind(Clusters.class).toInstance(mockClusters); + bind(OsFamily.class).toInstance(mockOSFamily); + } + }); + + + expect(mockDBDbAccessor.getConnection()).andReturn(mockConnection); + expect(mockConnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)).andReturn(mockStatement); + expect(mockStatement.executeQuery("select type_name from clusterconfig where type_name not in (select type_name from clusterconfigmapping)")).andReturn(mockResultSet); + + CheckDatabaseHelper checkDatabaseHelper = new CheckDatabaseHelper(mockDBDbAccessor, mockInjector, null); + + + easyMockSupport.replayAll(); + + mockAmbariMetainfo.init(); + checkDatabaseHelper.init(); + checkDatabaseHelper.checkForNotMappedConfigsToCluster(); + + easyMockSupport.verifyAll(); + } + + @Test + public void testCheckForConfigsSelectedMoreThanOnce() throws Exception { + EasyMockSupport easyMockSupport = new EasyMockSupport(); + final AmbariMetaInfo mockAmbariMetainfo = easyMockSupport.createNiceMock(AmbariMetaInfo.class); + final DBAccessor mockDBDbAccessor = easyMockSupport.createNiceMock(DBAccessor.class); + final Connection mockConnection = easyMockSupport.createNiceMock(Connection.class); + final ResultSet mockResultSet = easyMockSupport.createNiceMock(ResultSet.class); + final Statement mockStatement = easyMockSupport.createNiceMock(Statement.class); + + final StackManagerFactory mockStackManagerFactory = easyMockSupport.createNiceMock(StackManagerFactory.class); + final EntityManager mockEntityManager = easyMockSupport.createNiceMock(EntityManager.class); + final Clusters mockClusters = easyMockSupport.createNiceMock(Clusters.class); + final OsFamily mockOSFamily = easyMockSupport.createNiceMock(OsFamily.class); + final Injector mockInjector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(AmbariMetaInfo.class).toInstance(mockAmbariMetainfo); + bind(StackManagerFactory.class).toInstance(mockStackManagerFactory); + bind(EntityManager.class).toInstance(mockEntityManager); + bind(DBAccessor.class).toInstance(mockDBDbAccessor); + bind(Clusters.class).toInstance(mockClusters); + bind(OsFamily.class).toInstance(mockOSFamily); + } + }); + + + expect(mockDBDbAccessor.getConnection()).andReturn(mockConnection); + expect(mockConnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)).andReturn(mockStatement); + expect(mockStatement.executeQuery("select type_name from clusterconfigmapping group by type_name having sum(selected) > 1")).andReturn(mockResultSet); + + CheckDatabaseHelper checkDatabaseHelper = new CheckDatabaseHelper(mockDBDbAccessor, mockInjector, null); + + + easyMockSupport.replayAll(); + + mockAmbariMetainfo.init(); + checkDatabaseHelper.init(); + checkDatabaseHelper.checkForConfigsSelectedMoreThanOnce(); + + easyMockSupport.verifyAll(); + } + + @Test + public void testCheckForHostsWithoutState() throws Exception { + EasyMockSupport easyMockSupport = new EasyMockSupport(); + final AmbariMetaInfo mockAmbariMetainfo = easyMockSupport.createNiceMock(AmbariMetaInfo.class); + final DBAccessor mockDBDbAccessor = easyMockSupport.createNiceMock(DBAccessor.class); + final Connection mockConnection = easyMockSupport.createNiceMock(Connection.class); + final ResultSet mockResultSet = easyMockSupport.createNiceMock(ResultSet.class); + final Statement mockStatement = easyMockSupport.createNiceMock(Statement.class); + + final StackManagerFactory mockStackManagerFactory = easyMockSupport.createNiceMock(StackManagerFactory.class); + final EntityManager mockEntityManager = easyMockSupport.createNiceMock(EntityManager.class); + final Clusters mockClusters = easyMockSupport.createNiceMock(Clusters.class); + final OsFamily mockOSFamily = easyMockSupport.createNiceMock(OsFamily.class); + final Injector mockInjector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(AmbariMetaInfo.class).toInstance(mockAmbariMetainfo); + bind(StackManagerFactory.class).toInstance(mockStackManagerFactory); + bind(EntityManager.class).toInstance(mockEntityManager); + bind(DBAccessor.class).toInstance(mockDBDbAccessor); + bind(Clusters.class).toInstance(mockClusters); + bind(OsFamily.class).toInstance(mockOSFamily); + } + }); + + + expect(mockDBDbAccessor.getConnection()).andReturn(mockConnection); + expect(mockConnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)).andReturn(mockStatement); + expect(mockStatement.executeQuery("select host_name from hosts where host_id not in (select host_id from hoststate)")).andReturn(mockResultSet); + + CheckDatabaseHelper checkDatabaseHelper = new CheckDatabaseHelper(mockDBDbAccessor, mockInjector, null); + + + easyMockSupport.replayAll(); + + mockAmbariMetainfo.init(); + checkDatabaseHelper.init(); + checkDatabaseHelper.checkForHostsWithoutState(); + + easyMockSupport.verifyAll(); + } + + @Test + public void testCheckHostComponentStatesCountEqualsHostComponentsDesiredStates() throws Exception { + EasyMockSupport easyMockSupport = new EasyMockSupport(); + final AmbariMetaInfo mockAmbariMetainfo = easyMockSupport.createNiceMock(AmbariMetaInfo.class); + final DBAccessor mockDBDbAccessor = easyMockSupport.createNiceMock(DBAccessor.class); + final Connection mockConnection = easyMockSupport.createNiceMock(Connection.class); + final ResultSet mockResultSet = easyMockSupport.createNiceMock(ResultSet.class); + final Statement mockStatement = easyMockSupport.createNiceMock(Statement.class); + + final StackManagerFactory mockStackManagerFactory = easyMockSupport.createNiceMock(StackManagerFactory.class); + final EntityManager mockEntityManager = easyMockSupport.createNiceMock(EntityManager.class); + final Clusters mockClusters = easyMockSupport.createNiceMock(Clusters.class); + final OsFamily mockOSFamily = easyMockSupport.createNiceMock(OsFamily.class); + final Injector mockInjector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(AmbariMetaInfo.class).toInstance(mockAmbariMetainfo); + bind(StackManagerFactory.class).toInstance(mockStackManagerFactory); + bind(EntityManager.class).toInstance(mockEntityManager); + bind(DBAccessor.class).toInstance(mockDBDbAccessor); + bind(Clusters.class).toInstance(mockClusters); + bind(OsFamily.class).toInstance(mockOSFamily); + } + }); + + + expect(mockDBDbAccessor.getConnection()).andReturn(mockConnection); + expect(mockConnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)).andReturn(mockStatement); + expect(mockStatement.executeQuery("select count(*) from hostcomponentstate")).andReturn(mockResultSet); + expect(mockStatement.executeQuery("select count(*) from hostcomponentdesiredstate")).andReturn(mockResultSet); + expect(mockStatement.executeQuery("select count(*) FROM hostcomponentstate hcs " + + "JOIN hostcomponentdesiredstate hcds ON hcs.service_name = hcds.service_name AND " + + "hcs.component_name = hcds.component_name AND hcs.host_id = hcds.host_id")).andReturn(mockResultSet); + + CheckDatabaseHelper checkDatabaseHelper = new CheckDatabaseHelper(mockDBDbAccessor, mockInjector, null); + + + easyMockSupport.replayAll(); + + mockAmbariMetainfo.init(); + checkDatabaseHelper.init(); + checkDatabaseHelper.checkHostComponentStatesCountEqualsHostComponentsDesiredStates(); + + easyMockSupport.verifyAll(); + } + + @Test + public void testCheckServiceConfigs() throws Exception { + EasyMockSupport easyMockSupport = new EasyMockSupport(); + final AmbariMetaInfo mockAmbariMetainfo = easyMockSupport.createNiceMock(AmbariMetaInfo.class); + final DBAccessor mockDBDbAccessor = easyMockSupport.createNiceMock(DBAccessor.class); + final Connection mockConnection = easyMockSupport.createNiceMock(Connection.class); + final ResultSet mockResultSet = easyMockSupport.createNiceMock(ResultSet.class); + final ResultSet stackResultSet = easyMockSupport.createNiceMock(ResultSet.class); + final ResultSet serviceConfigResultSet = easyMockSupport.createNiceMock(ResultSet.class); + final Statement mockStatement = easyMockSupport.createNiceMock(Statement.class); + final ServiceInfo mockHDFSServiceInfo = easyMockSupport.createNiceMock(ServiceInfo.class); + + final StackManagerFactory mockStackManagerFactory = easyMockSupport.createNiceMock(StackManagerFactory.class); + final EntityManager mockEntityManager = easyMockSupport.createNiceMock(EntityManager.class); + final Clusters mockClusters = easyMockSupport.createNiceMock(Clusters.class); + final OsFamily mockOSFamily = easyMockSupport.createNiceMock(OsFamily.class); + final Injector mockInjector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(AmbariMetaInfo.class).toInstance(mockAmbariMetainfo); + bind(StackManagerFactory.class).toInstance(mockStackManagerFactory); + bind(EntityManager.class).toInstance(mockEntityManager); + bind(DBAccessor.class).toInstance(mockDBDbAccessor); + bind(Clusters.class).toInstance(mockClusters); + bind(OsFamily.class).toInstance(mockOSFamily); + } + }); + + Map services = new HashMap<>(); + services.put("HDFS", mockHDFSServiceInfo); + + Map>> configAttributes = new HashMap<>(); + configAttributes.put("core-site", new HashMap>()); + + expect(mockHDFSServiceInfo.getConfigTypeAttributes()).andReturn(configAttributes); + expect(mockAmbariMetainfo.getServices("HDP", "2.2")).andReturn(services); + expect(serviceConfigResultSet.next()).andReturn(true); + expect(serviceConfigResultSet.getString("service_name")).andReturn("HDFS"); + expect(serviceConfigResultSet.getString("type_name")).andReturn("core-site"); + expect(stackResultSet.next()).andReturn(true); + expect(stackResultSet.getString("stack_name")).andReturn("HDP"); + expect(stackResultSet.getString("stack_version")).andReturn("2.2"); + expect(mockDBDbAccessor.getConnection()).andReturn(mockConnection); + expect(mockConnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)).andReturn(mockStatement); + expect(mockStatement.executeQuery("select service_name from clusterservices where service_name not in (select service_name from serviceconfig where group_id is null)")).andReturn(mockResultSet); + expect(mockStatement.executeQuery("select service_name from serviceconfig where service_config_id not in (select service_config_id from serviceconfigmapping) and group_id is null")).andReturn(mockResultSet); + expect(mockStatement.executeQuery("select s.stack_name, s.stack_version from clusters c join stack s on c.desired_stack_id = s.stack_id")).andReturn(stackResultSet); + expect(mockStatement.executeQuery("select cs.service_name, type_name, sc.version from clusterservices cs " + + "join serviceconfig sc on cs.service_name=sc.service_name " + + "join serviceconfigmapping scm on sc.service_config_id=scm.service_config_id " + + "join clusterconfig cc on scm.config_id=cc.config_id " + + "where sc.group_id is null " + + "group by cs.service_name, type_name, sc.version")).andReturn(serviceConfigResultSet); + expect(mockStatement.executeQuery("select cs.service_name,cc.type_name from clusterservices cs " + + "join serviceconfig sc on cs.service_name=sc.service_name " + + "join serviceconfigmapping scm on sc.service_config_id=scm.service_config_id " + + "join clusterconfig cc on scm.config_id=cc.config_id " + + "join clusterconfigmapping ccm on cc.type_name=ccm.type_name and cc.version_tag=ccm.version_tag " + + "where sc.group_id is null and sc.service_config_id = (select max(service_config_id) from serviceconfig sc2 where sc2.service_name=sc.service_name) " + + "group by cs.service_name,cc.type_name " + + "having sum(ccm.selected) < 1")).andReturn(mockResultSet); + + CheckDatabaseHelper checkDatabaseHelper = new CheckDatabaseHelper(mockDBDbAccessor, mockInjector, null); + + + easyMockSupport.replayAll(); + + mockAmbariMetainfo.init(); + checkDatabaseHelper.init(); + checkDatabaseHelper.checkServiceConfigs(); + + easyMockSupport.verifyAll(); + } + + +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/58b0b501/ambari-server/src/test/python/TestAmbariServer.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/TestAmbariServer.py b/ambari-server/src/test/python/TestAmbariServer.py index fdc375b..511700b 100644 --- a/ambari-server/src/test/python/TestAmbariServer.py +++ b/ambari-server/src/test/python/TestAmbariServer.py @@ -94,6 +94,7 @@ with patch("platform.linux_distribution", return_value = os_distro_value): from ambari_server_main import get_ulimit_open_files, ULIMIT_OPEN_FILES_KEY, ULIMIT_OPEN_FILES_DEFAULT from ambari_server.serverClassPath import ServerClassPath from ambari_server.hostUpdate import update_host_names + from ambari_server.checkDatabase import check_database CURR_AMBARI_VERSION = "2.0.0" @@ -6816,6 +6817,42 @@ MIIFHjCCAwYCCQDpHKOBI+Lt0zANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJV pass + @patch.object(ServerClassPath, "get_full_ambari_classpath_escaped_for_shell", new = MagicMock(return_value = 'test' + os.pathsep + 'path12')) + @patch("ambari_server.serverUtils.is_server_runing") + @patch("ambari_commons.os_utils.run_os_command") + @patch("ambari_server.setupSecurity.generate_env") + @patch("ambari_server.setupSecurity.ensure_can_start_under_current_user") + @patch("ambari_server.serverConfiguration.read_ambari_user") + @patch("ambari_server.dbConfiguration.ensure_jdbc_driver_is_installed") + @patch("ambari_server.serverConfiguration.parse_properties_file") + @patch("ambari_server.serverConfiguration.get_ambari_properties") + @patch("ambari_server.serverConfiguration.get_java_exe_path") + def test_check_database(self, getJavaExePathMock, + getAmbariPropertiesMock, parsePropertiesFileMock, ensureDriverInstalledMock, readAmbariUserMock, + ensureCanStartUnderCurrentUserMock, generateEnvMock, runOSCommandMock, isServerRunningMock): + properties = Properties() + properties.process_pair("server.jdbc.database", "embedded") + + getJavaExePathMock.return_value = "/path/to/java" + getAmbariPropertiesMock.return_value = properties + readAmbariUserMock.return_value = "test_user" + ensureCanStartUnderCurrentUserMock.return_value = "test_user" + generateEnvMock.return_value = {} + runOSCommandMock.return_value = (0, "", "") + isServerRunningMock.return_value = (False, 1) + + check_database(properties) + + self.assertTrue(getJavaExePathMock.called) + self.assertTrue(readAmbariUserMock.called) + self.assertTrue(ensureCanStartUnderCurrentUserMock.called) + self.assertTrue(generateEnvMock.called) + + self.assertEquals(runOSCommandMock.call_args[0][0], '/path/to/java -cp test:path12 org.apache.ambari.server.checks.CheckDatabaseHelper' + ' > /var/log/ambari-server/ambari-server.log 2>&1') + + pass + @not_for_platform(PLATFORM_WINDOWS) @patch.object(OSCheck, "os_distribution", new = MagicMock(return_value = os_distro_value)) @patch.object(_ambari_server_, "is_server_runing")