org.apache.ambari.server.state.stack.upgrade.ConfigureTask.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ambari.server.state.stack.upgrade.ConfigureTask.java

Source

/**
 * 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.state.stack.upgrade;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.apache.ambari.server.serveraction.upgrades.ConfigureAction;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Config;
import org.apache.ambari.server.state.DesiredConfig;
import org.apache.ambari.server.state.stack.ConfigUpgradePack;
import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Condition;
import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.ConfigurationKeyValue;
import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Replace;
import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Transfer;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

/**
 * The {@link ConfigureTask} represents a configuration change. This task
 * contains id of change. Change definitions are located in a separate file (config
 * upgrade pack). IDs of change definitions share the same namespace within all
 * stacks
 * <p/>
 *
 * <pre>
 * {@code
 * <task xsi:type="configure" id="hdp_2_3_0_0-UpdateHiveConfig"/>
 * }
 * </pre>
 *
 */
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "configure")
public class ConfigureTask extends ServerSideActionTask {

    private static Logger LOG = LoggerFactory.getLogger(ConfigureTask.class);

    /**
     * The key that represents the configuration type to change (ie hdfs-site).
     */
    public static final String PARAMETER_CONFIG_TYPE = "configure-task-config-type";

    /**
     * Setting key/value pairs can be several per task, so they're passed in as a
     * json-ified list of objects.
     */
    public static final String PARAMETER_KEY_VALUE_PAIRS = "configure-task-key-value-pairs";

    /**
     * Transfers can be several per task, so they're passed in as a json-ified
     * list of objects.
     */
    public static final String PARAMETER_TRANSFERS = "configure-task-transfers";

    /**
     * Replacements can be several per task, so they're passed in as a json-ified list of
     * objects.
     */
    public static final String PARAMETER_REPLACEMENTS = "configure-task-replacements";

    public static final String actionVerb = "Configuring";

    /**
     * Gson
     */
    private Gson m_gson = new Gson();

    /**
     * Constructor.
     */
    public ConfigureTask() {
        implClass = ConfigureAction.class.getName();
    }

    private Task.Type type = Task.Type.CONFIGURE;

    @XmlAttribute(name = "id")
    public String id;

    /**
     * {@inheritDoc}
     */
    @Override
    public Type getType() {
        return type;
    }

    @Override
    public StageWrapper.Type getStageWrapperType() {
        return StageWrapper.Type.SERVER_SIDE_ACTION;
    }

    @Override
    public String getActionVerb() {
        return actionVerb;
    }

    /**
     * This getter is intended to be used only from tests. In production,
     * getConfigurationChanges() logic should be used instead
     * @return id of config upgrade change definition as defined in upgrade pack
     */
    public String getId() {
        return id;
    }

    /**
     * Gets the summary of the task or {@code null}.
     *
     * @return the task summary or {@code null}.
     */
    public String getSummary(ConfigUpgradePack configUpgradePack) {
        if (StringUtils.isNotBlank(id) && null != configUpgradePack) {
            ConfigUpgradeChangeDefinition definition = configUpgradePack.enumerateConfigChangesByID().get(id);
            if (null != definition && StringUtils.isNotBlank(definition.summary)) {
                return definition.summary;
            }
        }

        return super.getSummary();
    }

    /**
     * Gets a map containing the following properties pertaining to the
     * configuration value to change:
     * <ul>
     * <li>{@link #PARAMETER_CONFIG_TYPE} - the configuration type (ie hdfs-site)</li>
     * <li>{@link #PARAMETER_KEY_VALUE_PAIRS} - key/value pairs for the
     * configurations</li>
     * <li>{@link #PARAMETER_KEY_VALUE_PAIRS} - key/value pairs for the
     * configurations</li>
     * <li>{@link #PARAMETER_TRANSFERS} - COPY/MOVE/DELETE changes</li>
     * <li>{@link #PARAMETER_REPLACEMENTS} - value replacements</li>
     * </ul>
     *
     * @param cluster
     *          the cluster to use when retrieving conditional properties to test
     *          against (not {@code null}).
     * @return the a map containing the changes to make. This could potentially be
     *         an empty map if no conditions are met. Callers should decide how to
     *         handle a configuration task that is unable to set any configuration
     *         values.
     */
    public Map<String, String> getConfigurationChanges(Cluster cluster, ConfigUpgradePack configUpgradePack) {
        Map<String, String> configParameters = new HashMap<>();

        if (id == null || id.isEmpty()) {
            LOG.warn("Config task id is not defined, skipping config change");
            return configParameters;
        }

        if (configUpgradePack == null) {
            LOG.warn("Config upgrade pack is not defined, skipping config change");
            return configParameters;
        }

        // extract config change definition, referenced by current ConfigureTask
        ConfigUpgradeChangeDefinition definition = configUpgradePack.enumerateConfigChangesByID().get(id);
        if (definition == null) {
            LOG.warn(String.format("Can not resolve config change definition by id %s, " + "skipping config change",
                    id));
            return configParameters;
        }

        // the first matched condition will win; conditions make configuration tasks singular in
        // the properties that can be set - when there is a condition the task will only contain
        // conditions
        List<Condition> conditions = definition.getConditions();
        if (null != conditions && !conditions.isEmpty()) {
            for (Condition condition : conditions) {
                String conditionConfigType = condition.getConditionConfigType();
                String conditionKey = condition.getConditionKey();
                String conditionValue = condition.getConditionValue();

                // always add the condition's target type just so that we have one to
                // return even if none of the conditions match
                configParameters.put(PARAMETER_CONFIG_TYPE, condition.getConfigType());

                // check the condition; if it passes, set the configuration properties
                // and break
                String checkValue = getDesiredConfigurationValue(cluster, conditionConfigType, conditionKey);

                if (conditionValue.equals(checkValue)) {
                    List<ConfigurationKeyValue> configurations = new ArrayList<>(1);
                    ConfigurationKeyValue keyValue = new ConfigurationKeyValue();
                    keyValue.key = condition.getKey();
                    keyValue.value = condition.getValue();
                    configurations.add(keyValue);

                    configParameters.put(ConfigureTask.PARAMETER_KEY_VALUE_PAIRS, m_gson.toJson(configurations));

                    return configParameters;
                }
            }
        }

        // this task is not a condition task, so process the other elements normally
        if (null != definition.getConfigType()) {
            configParameters.put(PARAMETER_CONFIG_TYPE, definition.getConfigType());
        }

        // for every <set key=foo value=bar/> add it to this list
        if (null != definition.getKeyValuePairs() && !definition.getKeyValuePairs().isEmpty()) {
            configParameters.put(ConfigureTask.PARAMETER_KEY_VALUE_PAIRS,
                    m_gson.toJson(definition.getKeyValuePairs()));
        }

        // transfers
        List<Transfer> transfers = definition.getTransfers();
        if (null != transfers && !transfers.isEmpty()) {

            List<Transfer> allowedTransfers = new ArrayList<>();
            for (Transfer transfer : transfers) {
                if (transfer.operation == TransferOperation.DELETE) {
                    boolean ifKeyIsNotBlank = StringUtils.isNotBlank(transfer.ifKey);
                    boolean ifTypeIsNotBlank = StringUtils.isNotBlank(transfer.ifType);

                    //  value doesn't required for Key Check
                    if (ifKeyIsNotBlank && ifTypeIsNotBlank && transfer.ifKeyState == PropertyKeyState.ABSENT) {
                        boolean keyPresent = getDesiredConfigurationKeyPresence(cluster, transfer.ifType,
                                transfer.ifKey);
                        if (keyPresent) {
                            LOG.info("Skipping property delete for {}/{} as the key {} for {} is present",
                                    definition.getConfigType(), transfer.deleteKey, transfer.ifKey,
                                    transfer.ifType);
                            continue;
                        }
                    }

                    if (ifKeyIsNotBlank && ifTypeIsNotBlank && transfer.ifValue == null
                            && transfer.ifKeyState == PropertyKeyState.PRESENT) {
                        boolean keyPresent = getDesiredConfigurationKeyPresence(cluster, transfer.ifType,
                                transfer.ifKey);
                        if (!keyPresent) {
                            LOG.info("Skipping property delete for {}/{} as the key {} for {} is not present",
                                    definition.getConfigType(), transfer.deleteKey, transfer.ifKey,
                                    transfer.ifType);
                            continue;
                        }
                    }

                    if (ifKeyIsNotBlank && ifTypeIsNotBlank && transfer.ifValue != null) {

                        String ifConfigType = transfer.ifType;
                        String ifKey = transfer.ifKey;
                        String ifValue = transfer.ifValue;

                        String checkValue = getDesiredConfigurationValue(cluster, ifConfigType, ifKey);
                        if (!ifValue.toLowerCase().equals(StringUtils.lowerCase(checkValue))) {
                            // skip adding
                            LOG.info(
                                    "Skipping property delete for {}/{} as the value {} for {}/{} is not equal to {}",
                                    definition.getConfigType(), transfer.deleteKey, checkValue, ifConfigType, ifKey,
                                    ifValue);
                            continue;
                        }
                    }
                }
                allowedTransfers.add(transfer);
            }
            configParameters.put(ConfigureTask.PARAMETER_TRANSFERS, m_gson.toJson(allowedTransfers));
        }

        // replacements
        List<Replace> replacements = definition.getReplacements();
        if (null != replacements && !replacements.isEmpty()) {
            configParameters.put(ConfigureTask.PARAMETER_REPLACEMENTS, m_gson.toJson(replacements));
        }

        return configParameters;
    }

    /**
     * Gets the value of the specified cluster property.
     *
     * @param cluster
     *          the cluster (not {@code null}).
     * @param configType
     *          the configuration type (ie hdfs-site) (not {@code null}).
     * @param propertyKey
     *          the key to retrieve (not {@code null}).
     * @return the value or {@code null} if it does not exist.
     */
    private String getDesiredConfigurationValue(Cluster cluster, String configType, String propertyKey) {

        Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();
        DesiredConfig desiredConfig = desiredConfigs.get(configType);
        if (null == desiredConfig) {
            return null;
        }

        Config config = cluster.getConfig(configType, desiredConfig.getTag());
        if (null == config) {
            return null;
        }

        return config.getProperties().get(propertyKey);
    }

    /**
     * Gets the property presence state
     * @param cluster
     *          the cluster (not {@code null}).
     * @param configType
     *          the configuration type (ie hdfs-site) (not {@code null}).
     * @param propertyKey
     *          the key to retrieve (not {@code null}).
     * @return {@code true} if property key exists or {@code false} if not.
     */
    private boolean getDesiredConfigurationKeyPresence(Cluster cluster, String configType, String propertyKey) {

        Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();
        DesiredConfig desiredConfig = desiredConfigs.get(configType);
        if (null == desiredConfig) {
            return false;
        }

        Config config = cluster.getConfig(configType, desiredConfig.getTag());
        if (null == config) {
            return false;
        }
        return config.getProperties().containsKey(propertyKey);
    }

}