/**
* 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;
import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.VERSION;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.ambari.annotations.Experimental;
import org.apache.ambari.annotations.ExperimentalFeature;
import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
import org.apache.ambari.server.agent.ExecutionCommand.KeyNames;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
import org.apache.ambari.server.orm.entities.UpgradeEntity;
import org.apache.ambari.server.stack.MasterHostResolver;
import org.apache.ambari.server.stageplanner.RoleGraphFactory;
import org.apache.ambari.server.state.stack.UpgradePack;
import org.apache.ambari.server.state.stack.upgrade.Direction;
import org.apache.ambari.server.state.stack.upgrade.Grouping;
import org.apache.ambari.server.state.stack.upgrade.UpgradeScope;
import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
/**
* Used to hold various helper objects required to process an upgrade pack.
*/
public class UpgradeContext {
public static final String COMMAND_PARAM_VERSION = VERSION;
public static final String COMMAND_PARAM_CLUSTER_NAME = "clusterName";
public static final String COMMAND_PARAM_DIRECTION = "upgrade_direction";
public static final String COMMAND_PARAM_UPGRADE_PACK = "upgrade_pack";
public static final String COMMAND_PARAM_REQUEST_ID = "request_id";
public static final String COMMAND_PARAM_UPGRADE_TYPE = "upgrade_type";
public static final String COMMAND_PARAM_TASKS = "tasks";
public static final String COMMAND_PARAM_STRUCT_OUT = "structured_out";
public static final String COMMAND_DOWNGRADE_FROM_VERSION = "downgrade_from_version";
/**
* The original "current" stack of the cluster before the upgrade started.
* This is the same regardless of whether the current direction is
* {@link Direction#UPGRADE} or {@link Direction#DOWNGRADE}.
*/
public static final String COMMAND_PARAM_ORIGINAL_STACK = "original_stack";
/**
* The target upgrade stack before the upgrade started. This is the same
* regardless of whether the current direction is {@link Direction#UPGRADE} or
* {@link Direction#DOWNGRADE}.
*/
public static final String COMMAND_PARAM_TARGET_STACK = "target_stack";
/**
* The cluster that the upgrade is for.
*/
final private Cluster m_cluster;
/**
* The direction of the upgrade.
*/
final private Direction m_direction;
/**
* The type of upgrade.
*/
final private UpgradeType m_type;
/**
* The request parameters from the REST API for creating this upgrade.
*/
final private Map<String, Object> m_upgradeRequestMap;
/**
* The upgrade pack for this upgrade.
*/
private UpgradePack m_upgradePack;
/**
* The version being upgrade to or downgraded to.
*/
private final String m_version;
/**
* The original "current" stack of the cluster before the upgrade started.
* This is the same regardless of whether the current direction is
* {@link Direction#UPGRADE} or {@link Direction#DOWNGRADE}.
*/
private StackId m_originalStackId;
/**
* The stack currently used to start/restart services during an upgrade.This is the same
* During a {@link UpgradeType#ROLLING} upgrade, this is always the {@link this.m_targetStackId},
* During a {@link UpgradeType#NON_ROLLING} upgrade, this is initially the {@link this.m_sourceStackId} while
* stopping services, and then changes to the {@link this.m_targetStackId} when starting services.
*/
private StackId m_effectiveStackId;
/**
* The target upgrade stack before the upgrade started. This is the same
* regardless of whether the current direction is {@link Direction#UPGRADE} or
* {@link Direction#DOWNGRADE}.
*/
private StackId m_targetStackId;
private MasterHostResolver m_resolver;
private AmbariMetaInfo m_metaInfo;
private List<ServiceComponentHost> m_unhealthy = new ArrayList<>();
private Map<String, String> m_serviceNames = new HashMap<>();
private Map<String, String> m_componentNames = new HashMap<>();
private String m_downgradeFromVersion = null;
/**
* {@code true} if slave/client component failures should be automatically
* skipped. This will only automatically skip the failure if the task is
* skippable to begin with.
*/
private boolean m_autoSkipComponentFailures = false;
/**
* {@code true} if service check failures should be automatically skipped.
* This will only automatically skip the failure if the task is skippable to
* begin with.
*/
private boolean m_autoSkipServiceCheckFailures = false;
/**
* {@code true} if manual verification tasks should be automatically skipped.
*/
private boolean m_autoSkipManualVerification = false;
private Set<String> m_supported = new HashSet<>();
private UpgradeScope m_scope = UpgradeScope.ANY;
/**
* Used by some {@link Grouping}s to generate commands. It is exposed here
* mainly for injection purposes since the XML is not created by Guice.
*/
@Inject
private HostRoleCommandFactory m_hrcFactory;
/**
* Used by some {@link Grouping}s to determine command ordering. It is exposed
* here mainly for injection purposes since the XML is not created by Guice.
*/
@Inject
private RoleGraphFactory m_roleGraphFactory;
/**
* Used to lookup the reposotory version given a stack name and version.
*/
final private RepositoryVersionDAO m_repoVersionDAO;
/**
* Used for serializing the upgrade type.
*/
@Inject
private Gson m_gson;
/**
* Constructor.
*
* @param cluster
* the cluster that the upgrade is for
* @param type
* the type of upgrade, either rolling or non_rolling
* @param direction
* the direction for the upgrade
* @param upgradeRequestMap
* the original map of paramters used to create the upgrade
*
* @param repoVersionDAO
* the repository version DAO.
*/
@AssistedInject
public UpgradeContext(@Assisted Cluster cluster, @Assisted UpgradeType type,
@Assisted Direction direction, @Assisted String version,
@Assisted Map<String, Object> upgradeRequestMap,
RepositoryVersionDAO repoVersionDAO) {
m_repoVersionDAO = repoVersionDAO;
m_cluster = cluster;
m_type = type;
m_direction = direction;
m_version = version;
m_upgradeRequestMap = upgradeRequestMap;
// sets the original/target stacks - requires direction and cluster
setSourceAndTargetStacks();
}
/**
* Constructor.
*
* @param cluster
* the cluster that the upgrade is for
* @param upgradeEntity
* the upgrade entity
* @param repoVersionDAO
* the repository version DAO.
*/
@AssistedInject
public UpgradeContext(@Assisted Cluster cluster, @Assisted UpgradeEntity upgradeEntity,
RepositoryVersionDAO repoVersionDAO) {
m_repoVersionDAO = repoVersionDAO;
m_cluster = cluster;
m_type = upgradeEntity.getUpgradeType();
m_direction = upgradeEntity.getDirection();
m_version = upgradeEntity.getToVersion();
// sets the original/target stacks - requires direction and cluster
setSourceAndTargetStacks();
if (m_direction == Direction.DOWNGRADE) {
m_downgradeFromVersion = upgradeEntity.getFromVersion();
}
// since this constructor is initialized from an entity, then this map is
// not present
m_upgradeRequestMap = Collections.emptyMap();
}
/**
* Sets the source and target stack IDs. This will also set the effective
* stack ID based on the already-set {@link UpgradeType} and
* {@link Direction}.
*
* @see #getEffectiveStackId()
*/
private void setSourceAndTargetStacks() {
StackId sourceStackId = null;
// taret stack will not always be what it is today - tagging as experimental
@Experimental(feature = ExperimentalFeature.PATCH_UPGRADES)
StackId targetStackId = null;
switch (m_direction) {
case UPGRADE:
sourceStackId = m_cluster.getCurrentStackVersion();
RepositoryVersionEntity targetRepositoryVersion = m_repoVersionDAO.findByStackNameAndVersion(
sourceStackId.getStackName(), m_version);
// !!! TODO check the repo_version for patch-ness and restrict the
// context to those services that require it. Consult the version
// definition and add the service names to supportedServices
targetStackId = targetRepositoryVersion.getStackId();
break;
case DOWNGRADE:
sourceStackId = m_cluster.getCurrentStackVersion();
targetStackId = m_cluster.getDesiredStackVersion();
break;
}
m_originalStackId = sourceStackId;
switch (m_type) {
case ROLLING:
case HOST_ORDERED:
m_effectiveStackId = targetStackId;
break;
case NON_ROLLING:
m_effectiveStackId = (m_direction.isUpgrade()) ? sourceStackId : targetStackId;
break;
default:
m_effectiveStackId = targetStackId;
break;
}
m_targetStackId = targetStackId;
}
/**
* Gets the original mapping of key/value pairs from the request which created
* the upgrade.
*
* @return the original mapping of key/value pairs from the request which
* created the upgrade.
*/
public Map<String, Object> getUpgradeRequest() {
return m_upgradeRequestMap;
}
/**
* Gets the upgrade pack for this upgrade.
*
* @return the upgrade pack
*/
public UpgradePack getUpgradePack() {
return m_upgradePack;
}
/**
* Sets the upgrade pack for this upgrade
*
* @param upgradePack
* the upgrade pack to set
*/
public void setUpgradePack(UpgradePack upgradePack) {
m_upgradePack = upgradePack;
}
/**
* Gets the cluster that the upgrade is for.
*
* @return the cluster (never {@code null}).
*/
public Cluster getCluster() {
return m_cluster;
}
/**
* @return the target version for the upgrade
*/
public String getVersion() {
return m_version;
}
/**
* @return the direction of the upgrade
*/
public Direction getDirection() {
return m_direction;
}
/**
* @return the type of upgrade.
*/
public UpgradeType getType() {
return m_type;
}
/**
* Sets the host resolver.
*
* @param resolver
* the resolver that also references the required cluster
*/
public void setResolver(MasterHostResolver resolver) {
m_resolver = resolver;
}
/**
* @return the resolver
*/
public MasterHostResolver getResolver() {
return m_resolver;
}
/**
* @return the metainfo for access to service definitions
*/
public AmbariMetaInfo getAmbariMetaInfo() {
return m_metaInfo;
}
/**
* @param metaInfo the metainfo for access to service definitions
*/
public void setAmbariMetaInfo(AmbariMetaInfo metaInfo) {
m_metaInfo = metaInfo;
}
/**
* @param unhealthy a list of unhealthy host components
*/
public void addUnhealthy(List<ServiceComponentHost> unhealthy) {
m_unhealthy.addAll(unhealthy);
}
/**
* @return the originalStackId
*/
public StackId getOriginalStackId() {
return m_originalStackId;
}
/**
* @param originalStackId
* the originalStackId to set
*/
public void setOriginalStackId(StackId originalStackId) {
m_originalStackId = originalStackId;
}
/**
* @return the effectiveStackId that is currently in use.
*/
public StackId getEffectiveStackId() {
return m_effectiveStackId;
}
/**
* @param effectiveStackId the effectiveStackId to set
*/
public void setEffectiveStackId(StackId effectiveStackId) {
m_effectiveStackId = effectiveStackId;
}
/**
* @return the targetStackId
*/
public StackId getTargetStackId() {
return m_targetStackId;
}
/**
* @param targetStackId
* the targetStackId to set
*/
public void setTargetStackId(StackId targetStackId) {
m_targetStackId = targetStackId;
}
/**
* @return the service display name, or the service name if not set
*/
public String getServiceDisplay(String service) {
if (m_serviceNames.containsKey(service)) {
return m_serviceNames.get(service);
}
return service;
}
/**
* @return the component display name, or the component name if not set
*/
public String getComponentDisplay(String service, String component) {
String key = service + ":" + component;
if (m_componentNames.containsKey(key)) {
return m_componentNames.get(key);
}
return component;
}
/**
* @param service the service name
* @param displayName the display name for the service
*/
public void setServiceDisplay(String service, String displayName) {
m_serviceNames.put(service, (displayName == null) ? service : displayName);
}
/**
* @param service the service name that owns the component
* @param component the component name
* @param displayName the display name for the component
*/
public void setComponentDisplay(String service, String component, String displayName) {
String key = service + ":" + component;
m_componentNames.put(key, displayName);
}
/**
* This method returns the non-finalized version we are downgrading from.
*
* @return version cluster is downgrading from
*/
public String getDowngradeFromVersion() {
return m_downgradeFromVersion;
}
/**
* Set the HDP stack version we are downgrading from.
*
* @param downgradeFromVersion
*/
public void setDowngradeFromVersion(String downgradeFromVersion) {
m_downgradeFromVersion = downgradeFromVersion;
}
/**
* Gets whether skippable components that failed are automatically skipped.
*
* @return the skipComponentFailures
*/
public boolean isComponentFailureAutoSkipped() {
return m_autoSkipComponentFailures;
}
/**
* Sets whether skippable components that failed are automatically skipped.
*
* @param autoSkipComponentFailures
* {@code true} to automatically skip component failures which are
* marked as skippable.
*/
public void setAutoSkipComponentFailures(boolean autoSkipComponentFailures) {
m_autoSkipComponentFailures = autoSkipComponentFailures;
}
/**
* Gets whether skippable service checks that failed are automatically
* skipped.
*
* @return the skipServiceCheckFailures
*/
public boolean isServiceCheckFailureAutoSkipped() {
return m_autoSkipServiceCheckFailures;
}
/**
* Sets whether skippable service checks that failed are automatically
* skipped.
*
* @param autoSkipServiceCheckFailures
* {@code true} to automatically skip service check failures which
* are marked as being skippable.
*/
public void setAutoSkipServiceCheckFailures(boolean autoSkipServiceCheckFailures) {
m_autoSkipServiceCheckFailures = autoSkipServiceCheckFailures;
}
/**
* Gets whether manual verification tasks can be automatically skipped.
*
* @return the skipManualVerification
*/
public boolean isManualVerificationAutoSkipped() {
return m_autoSkipManualVerification;
}
/**
* Sets whether manual verification checks are automatically skipped.
*
* @param autoSkipManualVerification
* {@code true} to automatically skip manual verification tasks.
*/
public void setAutoSkipManualVerification(boolean autoSkipManualVerification) {
m_autoSkipManualVerification = autoSkipManualVerification;
}
/**
* Sets the service names that are supported by an upgrade. This is used for
* {@link RepositoryType#PATCH} and {@link RepositoryType#SERVICE}.
*
* @param services the set of specific services
*/
@Experimental(feature=ExperimentalFeature.PATCH_UPGRADES)
public void setSupportedServices(Set<String> services) {
m_supported = services;
}
/**
* @return the set of supported services, or an empty set if ALL services
* are supported
*/
@Experimental(feature=ExperimentalFeature.PATCH_UPGRADES)
public Set<String> getSupportedServices() {
return Collections.unmodifiableSet(m_supported);
}
/**
* Gets if a service is supported. If there are no services marked for the context,
* then ALL services are supported
* @param serviceName the service name to check.
* @return {@code true} when the service is supported
*/
@Experimental(feature=ExperimentalFeature.PATCH_UPGRADES)
public boolean isServiceSupported(String serviceName) {
if (m_supported.isEmpty() || m_supported.contains(serviceName)) {
return true;
}
return false;
}
@Experimental(feature=ExperimentalFeature.PATCH_UPGRADES)
public void setScope(UpgradeScope scope) {
m_scope = scope;
}
@Experimental(feature=ExperimentalFeature.PATCH_UPGRADES)
public boolean isScoped(UpgradeScope scope) {
return m_scope.isScoped(scope);
}
/**
* Gets the injected instance of a {@link RoleGraphFactory}.
*
* @return a {@link RoleGraphFactory} instance (never {@code null}).
*/
public RoleGraphFactory getRoleGraphFactory() {
return m_roleGraphFactory;
}
/**
* Gets the injected instance of a {@link HostRoleCommandFactory}.
*
* @return a {@link HostRoleCommandFactory} instance (never {@code null}).
*/
public HostRoleCommandFactory getHostRoleCommandFactory() {
return m_hrcFactory;
}
/**
* Gets a map initialized with parameters required for upgrades to work. The
* following properties are already set:
* <ul>
* <li>{@link #COMMAND_PARAM_CLUSTER_NAME}
* <li>{@link #COMMAND_PARAM_VERSION}
* <li>{@link #COMMAND_PARAM_DIRECTION}
* <li>{@link #COMMAND_PARAM_ORIGINAL_STACK}
* <li>{@link #COMMAND_PARAM_TARGET_STACK}
* <li>{@link #COMMAND_DOWNGRADE_FROM_VERSION}
* <li>{@link #COMMAND_PARAM_UPGRADE_TYPE}
* <li>{@link KeyNames#REFRESH_CONFIG_TAGS_BEFORE_EXECUTION} - necessary in
* order to have the commands contain the correct configurations. Otherwise,
* they will contain the configurations that were available at the time the
* command was created. For upgrades, this is problematic since the commands
* are all created ahead of time, but the upgrade may change configs as part
* of the upgrade pack.</li>
* <ul>
*
* @return the initialized parameter map.
*/
public Map<String, String> getInitializedCommandParameters() {
Map<String, String> parameters = new HashMap<>();
parameters.put(COMMAND_PARAM_CLUSTER_NAME, m_cluster.getClusterName());
parameters.put(COMMAND_PARAM_VERSION, getVersion());
parameters.put(COMMAND_PARAM_DIRECTION, getDirection().name().toLowerCase());
parameters.put(COMMAND_PARAM_ORIGINAL_STACK, getOriginalStackId().getStackId());
parameters.put(COMMAND_PARAM_TARGET_STACK, getTargetStackId().getStackId());
parameters.put(COMMAND_DOWNGRADE_FROM_VERSION, getDowngradeFromVersion());
if (null != getType()) {
// use the serialized attributes of the enum to convert it to a string,
// but first we must convert it into an element so that we don't get a
// quoted string - using toString() actually returns a quoted stirng which
// is bad
JsonElement json = m_gson.toJsonTree(getType());
parameters.put(COMMAND_PARAM_UPGRADE_TYPE, json.getAsString());
}
parameters.put(KeyNames.REFRESH_CONFIG_TAGS_BEFORE_EXECUTION, "true");
return parameters;
}
}